将子类的枚举器强制转换为父类的枚举器是错误的吗?
-
04-07-2019 - |
题
我的构建中出现了错误:
错误12无法隐式转换 类型 <!>“System.Collections.Generic.IEnumerator LT; <!> BaseClass的GT;” 至 <!>“System.Collections.Generic.IEnumerator LT; <!> IParentClass GT;”。 存在显式转换(是你 错过演员?)
简单地把它丢掉是不对的?
这是我的代码:
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及以下界面方差不受支持。请参阅Eric Lippert关于此的优秀系列,特别是这个。
不,它不安全,见下文:
使用System.Collections.Generic; Foo {} class Bar: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
s列表应该能够收到ChildThing2
。但是ChildThing1
的列表无法处理List<ChildThing1>
,或者除了List<IParent>
之外的<=>的任何实现者 - 换句话说,如果<=>被允许转换为<=>,它会必须能够处理的所有子类,而不仅仅是<=>和<=>。
请注意,Java泛型有一种方式可以说<!>;我想要一个从<!>引用继承的东西的列表;除了<!>之外,我想要一个这个继承的东西的列表,<!> quot;这使得一些问题的解决方案更加有趣(在我看来更优雅)。
IEnumerator<BaseClass>
和IEnumerator<ParentClass>
是无关的,尽管它们的通用参数是。我会使用像这样的LINQ Select
扩展方法:
return this.Select(x => (IParentClass)x).GetEnumerator();
或Cast
扩展方法:
return this.Cast<IParentClass>().GetEnumerator();