Why does a function that takes IEnumerable<interface> not accept IEnumerable<class>?
比如说,我有一门课:
1 2 3 4
| public class MyFoo : IMyBar
{
...
} |
然后,我想使用以下代码:
1 2 3 4 5 6
| 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); |
但这会产生错误:
1
| `Argument '1': cannot convert from 'IEnumerable<MyFoo>' to 'IEnumerable<IMyBar>' |
为什么会这样?由于MyFoo实现了IMYBAR,因此可以将MyFoo的IEnumerable视为IMYBAR的IEnumerable。一个平凡的现实世界的例子是,生产一份汽车清单,然后被告知它不是一份汽车清单。
这只是一个小小的烦恼,但如果有人能对此有所了解,我将非常感激。
- 这个问题也包括了你想要的答案:stackoverflow.com/questions/2346763/&hellip;
- 是的,谢谢-我曾经在"相关"列表中看到过这个问题,但在我写的时候没有出现…
这将在C 4.0中工作!你所说的是一般协方差。
同时可以使用Cast扩展方法:
1
| List<IMyBar> interfaceList = new List<IMyBar>(classList.Cast<IMyBar>()); |
- 好吧,太棒了,但它看起来并不像火箭科学——有没有什么特别的原因以前不支持它?
- @马特:这不是火箭科学,但和其他功能一样,它需要时间和金钱来实现,而且有一些具有更高优先级的伟大功能,它们实现得更快(例如,Linq确实很好,不是吗?)。您必须削减一些伟大的功能才能及时发货。--关于这个特定的特性,需要注意的一点是它需要底层clr的支持,而clr 2.0不支持它。在clr 4.0中添加了支持。
- 谢谢-非常有帮助…:)
- 实际上,在clr 2.0中实现这一功能需要大约两秒钟的时间:stackoverflow.com/questions/1995113/StrangestLanguage Featu&zwnj;&8203;re/&hellip;这只是他们的疏忽。
- @与许多语言特征相比,协方差几乎是一门火箭科学。一点也不简单!
- @BlueRaja:构造函数不能采用泛型类型参数。
- @丹:好吧,我同意向一种语言添加协方差需要很多思考(你必须对你选择的协方差类型以及如何实现它做出很多选择)。我认为C的人做得很好。我说这不是火箭科学,因为它是计算机科学和类型理论中解决的问题。
- @blueraja:事实上,它已经在clr 2.0中得到了支持——它只是不在c 2.0(或3.0)中。我不会像你们想象的那样认为这是一个解决了的问题-即使在.NET 4.0中,在组合不同的委托时也会有问题…
- 为了澄清:在clr v2.0中添加了对变量类型定义的支持。在C 4中添加了对在C中定义和使用此类的支持。在clr 4中对bcl类型进行注释以使用此功能。对于那些认为这很简单的人来说,我希望你们能阅读到到目前为止该特性已经生成的数百页错误报告。这种激进的类型系统的改变并不容易;一个学者能够证明代数的良好工作与如何使它与现有的数百万行代码体一起工作的问题完全无关。
- @埃里克:谢谢你的澄清。为了便于实施,我相信你是对的,基本上我在我上次的评论中也说过同样的话。从来没有想过低估你们在C4的出色表现。我希望这不是我说的话的解释。
- Mehrdad,我认为Eric正在回应BlueRaja的评论,他说实现这个功能需要2秒钟。讽刺的是,当你认为他只花了10秒(5倍的数量)就输入了一个功能的评论,但预计功能本身需要2秒。
- @埃里克·利珀特——只是为了澄清——我不是说这很容易——我是说这似乎很容易——非常感谢你的智慧,这让我有了更深刻的理解:)
- @埃里克:我刚才说的是addrange能够支持添加扩展t的类,而不是一般的co/contra方差。我意识到这个话题有多么复杂,我很感激你们为实现它所付出的巨大努力。
要澄清的是,它现在不起作用的原因是IEnumerable不是从IEnumerable继承的。我再说一遍:派生类型的可枚举不从基类型的可枚举继承。相反,这两种类型都专门化了IEnumerable泛型类型。在.NET 3.5及更低版本中,它们除了专门化(不是继承)之外没有其他关系,否则在类型系统中是两种完全不同的类型。
.NET 4.0根本不会更改这些类型的关联方式。它们仍然是两种完全不同的类型,只通过专门化来联系。它将要做的是允许您编写了解专门化关系的方法,并且在某些情况下允许您在编写另一个方法时替换一个方法。
- 为了澄清你的第二段:一般性差异的要点是它改变了某些一般性类型上的"is assignment compatible with"关系。这就是相关的关系;正如你所说,继承关系是不变的。
- 乔尔,我从来没有这样想过共同方差/反向方差。非常有用的答案,+1来自我。
这在.NET 4.0中受支持,但不早于.NET 4.0。
http://msdn.microsoft.com/en-us/library/dd799517%28与100%29.aspx
- 从技术上讲,它从2.0开始就通过IL在.NET中得到了支持,但它只在4.0中获得了C和vb.net的支持。
- @贾雷德帕:真的吗?clr 2.0是否支持可验证IL中的一般协方差?
- @是的,我想是的。我和Lucian讨论这个问题已经1年多了,但我现在的记忆是,从2.0开始,clr支持co/contravariance,而4.0都在编译器中(可能是clr中的一个或两个bug修复程序)。
- @Jaredpar很有趣。乔恩在这里也是这么说的。我从没想过会是这样。
- @是的,但是在2.0bcl中IEnumerable没有标记为协变。
如果您想在IEnumerable(如您在问题标题中所写)和列表(如您在问题中所写)之间进行强制转换,可以等待C 4.0协方差特性。在那之前,你可以使用扩展方法。但我不会使用其他答案中提到的强制转换扩展方法,而是编写自己的方法,它只能用于IEnumerable中的协方差强制转换。当/如果您切换到C 4.0,您将很容易在代码中找到强制转换多余的所有位置。
1 2 3 4 5 6 7 8 9 10 11
| 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;
}
}
} |
最后节点。对于NetFramework 4.0,似乎没有预编译器常量,否则我将添加
1 2 3
| #if DOTNET4
[Obsolete("You can directly cast in C# 4.0.")]
#endif |