将子类的枚举数强制转换为父类的枚举器是错误的吗
本文关键字:枚举 父类 错误 子类 转换 | 更新日期: 2023-09-27 17:48:52
我的构建中有一个错误,上面写着:
错误12无法隐式转换类型'System.Collections.Generic.IEnumerator<BaseClass>'到'System.Collections.Generic.IEnumerator<IParentClass>'。存在显式转换(您是缺少演员阵容?)
简单地抛弃它是错误的吗?
这是我的代码:
public Dictionary<Int32, BaseClass> Map { get; private set; }
public IEnumerator<BaseClass> GetEnumerator()
{
return this.Map.Values.GetEnumerator();
}
public IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
{
return this.GetEnumerator(); // ERROR!
}
我的问题是,我可以改一下这行吗:
return this.GetEnumerator();
至:
return (IEnumerator<IParentClass>)this.GetEnumerator();
(没有任何不良副作用)?
接受的答案:
我已经将功能更改为以下(在阅读Jon Skeet的帖子后):
IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
{
return this.Map.Values.Cast<IParentClass>().GetEnumerator();
}
不,你不能,因为泛型目前在C#中不是协变的。NET本身有一些支持(对委托和接口),但它还没有真正使用。
如果您返回的是IEnumerable<BaseClass>
而不是IEnumerator<BaseClass>
(假设.NEt 3.5),则可以使用Enumerable.Cast
,但您目前需要编写自己的扩展方法,例如
public static IEnumerator<TParent> Upcast<TParent, TChild>
(this IEnumerator<TChild> source)
where TChild : TParent
{
while (source.MoveNext())
{
yield return source.Current;
}
}
或者,在您的情况下,您可以更早地使用Cast:
return this.Map.Values.Cast<BaseClass>().GetEnumerator();
不,你不能,至少在C#3.0及以下版本中不支持接口差异。看看埃里克·利珀特关于这个的精彩系列,特别是这一个。
不,它不安全,请参阅以下内容:
使用System.Collections.Generic;类Foo{}类酒吧:Foo{}
static class Program
{
static IEnumerator<Foo> GetBase() {
yield return new Foo();
yield return new Bar();
}
static IEnumerator<Bar> GetDerived()
{
return (IEnumerator<Bar>)GetBase();
}
static void Main()
{
var obj = GetDerived(); // EXCEPTION
}
}
但是,您应该能够使用迭代器块为您执行强制转换吗?
static IEnumerator<Bar> GetDerived()
{
using (IEnumerator<Foo> e = GetBase())
{
while (e.MoveNext())
{
// or use "as" and only return valid data
yield return (Bar)e.Current;
}
}
}
要解释为什么这不合适,请用图片代替Enumerator
,List
。两者都使用泛型——编译器不以与泛型参数相关的特殊方式处理任何一个。
void doStuff() {
List<IParentThing> list = getList();
list.add(new ChildThing2());
}
List<IParentThing> getList() {
return new List<ChildThing1>(); //ERROR!
}
第一种方法很好——IParentThing
的列表应该能够接收到ChildThing2
。但是ChildThing1
的列表不能处理ChildThing2
,或者ChildThing1
之外的IParentThing
的任何实现者——换句话说,如果允许List<ChildThing1>
转换为List<IParent>
,则它必须能够处理IParentThing
的所有子类,而不仅仅是IParentThing
和ChildThing1
。
注意,Java泛型除了"我想要一个继承的列表"之外,还有一种方式可以说"我想要从中继承的任何东西的列表",这允许对一些问题提供更有趣(在我看来也是优雅的)的解决方案。
IEnumerator<BaseClass>
和IEnumerator<ParentClass>
是不相关的,尽管它们的通用参数是。相反,我会使用LINQ Select
扩展方法,如下所示:
return this.Select(x => (IParentClass)x).GetEnumerator();
或Cast
扩展方法:
return this.Cast<IParentClass>().GetEnumerator();