将子类的枚举器强制转换为父类的枚举器是错误的吗?

时间:2008-10-23 13:12:01

标签: c# .net generics covariance

我的构建中出现了错误:

  

错误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();
        }

5 个答案:

答案 0 :(得分:5)

不,你不能,因为仿制品目前在C#中并不协变。 .NET本身有一些支持(对于委托和接口)但它还没有真正使用过。

如果您要返回IEnumerable<BaseClass>而不是IEnumerator<BaseClass>(并假设.NEt 3.5),您可以使用Enumerable.Cast - 但您目前需要编写自己的扩展方法,例如< / p>

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();

答案 1 :(得分:2)

不,你不能,至少在C#3.0及以下界面方差不受支持。请参阅Eric Lippert关于此的精彩系列,特别是this one

答案 2 :(得分:0)

IEnumerator<BaseClass>IEnumerator<ParentClass>是无关的,尽管它们的通用参数是。我会改为使用LINQ Select扩展方法:

return this.Select(x => (IParentClass)x).GetEnumerator();

Cast扩展方法:

return this.Cast<IParentClass>().GetEnumerator();

答案 3 :(得分:0)

不,它不安全,见下文:

使用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;
        }
    }
}

答案 4 :(得分:0)

要理解为什么这是不合适的,请拍照而不是EnumeratorList。两者都使用泛型 - 编译器不会以与通用参数相关的特殊方式处理任何一个。

void doStuff() {
    List<IParentThing> list = getList();
    list.add(new ChildThing2());
}

List<IParentThing> getList() {
    return new List<ChildThing1>();  //ERROR!
}

第一种方法很好 - IParentThing的列表应该能够接收ChildThing2。但是ChildThing1的列表无法处理ChildThing2,或者IParentThing除了ChildThing1以外的任何实现者 - 换句话说,如果允许List&lt;ChildThing1>要转换为List&lt;IParent>,它必须能够处理IParentThing所有子类,而不仅仅是IParentThingChildThing1。< / p>

请注意,Java泛型有一种方式可以说“我想要一个从此继承的东西的列表”,除了“我想要一个这个继承的东西的列表”,这样可以让我更有意思(在我看来)优雅)解决一些问题。