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

本文关键字:枚举 父类 错误 子类 转换 | 更新日期: 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;
        }
    }
}

要解释为什么这不合适,请用图片代替EnumeratorList。两者都使用泛型——编译器不以与泛型参数相关的特殊方式处理任何一个。

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&lt;ChildThing1>转换为List&lt;IParent>,则它必须能够处理IParentThing所有子类,而不仅仅是IParentThingChildThing1

注意,Java泛型除了"我想要一个继承的列表"之外,还有一种方式可以说"我想要从中继承的任何东西的列表",这允许对一些问题提供更有趣(在我看来也是优雅的)的解决方案。

IEnumerator<BaseClass>IEnumerator<ParentClass>是不相关的,尽管它们的通用参数是。相反,我会使用LINQ Select扩展方法,如下所示:

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

Cast扩展方法:

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