3

我的构建中有一个错误,上面写着:

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

5 回答 5

5

不,你不能,因为泛型目前在 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();
于 2008-10-23T13:16:41.240 回答
2

不,您不能,至少在 C# 3.0 及以下版本中不支持接口变化。请参阅 Eric Lippert 的优秀系列,特别是这个

于 2008-10-23T13:16:07.730 回答
0

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

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

Cast扩展方法:

return this.Cast<IParentClass>().GetEnumerator();
于 2008-10-23T13:15:49.197 回答
0

不,这不安全,请参见下文:

使用 System.Collections.Generic;类 Foo { } 类 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;
        }
    }
}
于 2008-10-23T13:19:17.147 回答
0

解释为什么这不合适,请使用图片而不是 a Enumerator, a List。两者都使用泛型 - 编译器不会以与泛型参数相关的特殊方式处理任何一个。

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

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

第一种方法很好——一个IParentThings 列表应该能够接收一个ChildThing2. 但是ChildThing1s 的列表不能处理 a ChildThing2,或者实际上任何IParentThing其他的实现者ChildThing1——换句话说,如果List&lt;ChildThing1>允许将 a 转换为 a List&lt;IParent>,它就必须能够处理 的所有子类IParentThing,而不仅仅是IParentThingand ChildThing1

请注意,Java 泛型除了“我想要一个继承自它的任何东西的列表”之外,还有一种说法是“我想要一个继承自 this 的任何东西的列表”,这允许更有趣(并且在我看来优雅)的解决方案对于一些问题。

于 2008-10-23T13:22:59.400 回答