【问题标题】:Why does a function that takes IEnumerable<interface> not accept IEnumerable<class>?为什么采用 IEnumerable<interface> 的函数不接受 IEnumerable<class>?
【发布时间】:2011-01-30 13:17:06
【问题描述】:

比如说,我有一个班级:

public class MyFoo : IMyBar
{
    ...
}

然后,我想使用以下代码:

List<MyFoo> classList = new List<MyFoo>();
classList.Add(new MyFoo(1));
classList.Add(new MyFoo(2));
classList.Add(new MyFoo(3));

List<IMyBar> interfaceList = new List<IMyBar>(classList);

但这会产生错误:

`Argument '1': cannot convert from 'IEnumerable<MyFoo>' to 'IEnumerable<IMyBar>' 

这是为什么?由于 MyFoo 实现了 IMyBar,人们会期望 MyFoo 的 IEnumerable 可以被视为 IMyBar 的 IEnumerable。一个平凡的现实世界示例正在生成汽车列表,然后被告知这不是车辆列表。

这只是一个小烦恼,但如果有人能对此有所了解,我将不胜感激。

【问题讨论】:

  • 这个问题也涵盖了你想要的答案:stackoverflow.com/questions/2346763/…
  • 是的,谢谢 - 我在发布问题后在“相关”列表中看到了它,但在我写它的时候它没有出现......

标签: generics inheritance c#-3.0 ienumerable covariance


【解决方案1】:

如果您想在 IEnumerables(正如您在问题的标题中所写)而不是 Lists(如您在问题中所写)之间进行转换,您可以等待 c# 4.0 协方差功能。 在此之前,您可以使用扩展方法。 但我不会使用其他答案中提到的Cast extension method,而是编写我自己的方法,该方法只能用于 IEnumerable 中的协方差转换。当/如果您切换到 C# 4.0,您将很容易在代码中找到所有需要强制转换的地方。

public static class cEnumerableExtensions
{
    public static IEnumerable<TResult> CovarianceConversion<TResult, TSource>(this IEnumerable<TSource> source)
        where TSource : TResult
    {
        foreach (var item in source)
        {
            yield return item;
        }
    }
}

最终节点。好像有no precompiler constant for the netframework 4.0,不然我就加了

    #if DOTNET4
    [Obsolete("You can directly cast in C# 4.0.")]
    #endif

【讨论】:

    【解决方案2】:

    澄清一下,它现在不起作用的原因是IEnumerable&lt;MyClass&gt; 不是从IEnumerable&lt;MyBar&gt; 继承的。我再说一遍:派生类型的可枚举对象不是从基类型的可枚举对象继承的。相反,这两种类型都专门化了IEnumerable&lt;T&gt; 泛型类型。在 .Net 3.5 及更低版本中,它们除了专业化(不是继承)之外没有其他关系,并且在类型系统中是两种完全不同的类型。

    .Net 4.0 根本不会改变这些类型的关联方式。它们仍将是仅通过专业化相关的两种完全不同的类型。它将允许您编写了解专业化关系的方法,并且在某些情况下允许您在编写另一个时替换一个。

    【讨论】:

    • 澄清你的第二段:泛型差异的关键在于它改变了某些泛型类型的“分配兼容”关系。这就是相关的关系;正如你所说,继承关系没有改变。
    • 乔尔,我从来没有这样想过协方差/反方差。非常有用的答案,我 +1。
    【解决方案3】:

    .NET 4.0 支持此功能,但更早版本不支持。

    http://msdn.microsoft.com/en-us/library/dd799517%28VS.100%29.aspx

    【讨论】:

    • 从技术上讲,它从 2.0 开始就通过 IL 在 .Net 中得到支持,但它只在 4.0 中获得了 C# 和 VB.Net 的支持
    • @JaredPar:真的吗? CLR 2.0 是否支持 verifiable IL 中的通用协方差?
    • @Mehrdad,是的,我相信是的。自从我与 Lucian 讨论以来已经 1 年多,但我目前的回忆是 CLR 从 2.0 开始就支持协/逆变,并且 4.0 都在编译器中(可能是 CLR 中的一两个错误修复)。
    • @JaredPar 很有趣。乔恩在这里说同样的话。我从没想过会是这样。
    • @Mehrdad - 是的,但 IEnumerable&lt;T&gt; 在 2.0 BCL 中未标记为协变。
    【解决方案4】:

    这将在 C# 4.0 中工作!您所说的称为泛型协方差

    在此期间您可以使用Cast extension method

    List<IMyBar> interfaceList = new List<IMyBar>(classList.Cast<IMyBar>());
    

    【讨论】:

    • 好的,太棒了,但它看起来并不像火箭科学——有什么特别的原因导致它以前不受支持吗?
    • @Matt:这不是火箭科学,但就像任何功能一样,实现它需要时间和金钱,并且有一些具有更高优先级的很棒的功能他们已经更快地实现了(例如 LINQ 真的很好,是不是吗?)。您必须削减一些出色的功能才能及时发布产品。 -- 关于这个特定功能需要注意的一点是,它需要底层 CLR 的支持,而 CLR 2.0 不支持它。在 CLR 4.0 中添加了支持。
    • 实际上,他们在 CLR 2.0 中实施大约需要两秒钟:stackoverflow.com/questions/1995113/strangest-language-feature/… 这只是他们的疏忽。
    • 澄清一下:CLR v2.0 添加了对变量类型定义的支持。在 C# 4 中添加了对在 C# 中定义和使用此类的支持。在 CLR 4 中完成了使用此功能的注释 BCL 类型。对于那些认为这很容易的人,我希望你能阅读数百页的错误报告该功能到目前为止已生成。改变这种激进的类型系统不容易;学者可以证明代数很好地工作,这与如何使其与现有的数百万行代码一起工作的问题完全无关。
    • Mehrdad,我认为 Eric 正在回应 BlueRaja 的评论,他说实现此功能需要 2 秒。当您认为他只需要 10 秒(数量的 5 倍)来输入对某个功能的评论,但预计该功能本身将需要 2 秒时,这具有讽刺意味。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    相关资源
    最近更新 更多