C#无法从IEnumerable 转换为IEnumerable

我最近在尝试将AddRange(IEnumerable)添加到List时遇到了麻烦。 可能是一个经典问题,但我还没有真正得到它。

我知道期望List参数的方法不满足List,因为他们可能会尝试向List添加Base,这显然是不可能的。

但是,如果我得到正确的,因为IEnumerables本身不能改变,它应该在这种情况下工作。

我想到的代码看起来像这样:

class Foo { } class Bar : Foo { } class FooCol { private List m_Foos = new List (); public void AddRange1(IEnumerable foos) { m_Foos.AddRange (foos); // does work } public void AddRange2(IEnumerable foos) where T : Foo { m_Foos.AddRange (foos); // does not work } } class Program { static void Main(string[] args) { FooCol fooCol = new FooCol (); List foos = new List (); List bars = new List (); fooCol.AddRange1 (foos); // does work fooCol.AddRange1 (bars); // does not work fooCol.AddRange2 (foos); // does work fooCol.AddRange2 (bars); // does work } } 

我试图在AddRange2方法中向编译器传递一个提示,但这只是问题所在。

我的思维方式有缺陷吗? 这是语言的限制还是设计?

IIRC,Java 1.5中添加了对此类操作的支持,所以也许它将在未来的某个时候添加到C#中……

这是协方差,将在C#4.0 / .NET 4.0中修复。 目前,通用选项是最佳答案(对于IEnumerable – 而不是IList等 )。

但是在通用方法中,你必须用T来思考。 您还可以使用CastOfType与LINQ实现类似的function。

在C#3.0中,您可以使用“Cast”扩展方法。 如果您导入System.Linq然后使用此代码:

 public void AddRange2(IEnumerable foos) where T : Foo { m_Foos.AddRange (foos.Cast()); } 

那它应该适合你。

有扩展方法的解决方法:

 public static IEnumerable ToBaseEnumerable( this IEnumerable items ) where TDerived : TBase { foreach( var item in items ) { yield return item; } } ... IEnumerable employees = GetEmployees(); //Emplyoee derives from Person DoSomethingWithPersons( employees.ToBaseEnumerable() ); 

但是“”有点尴尬:/。

当然,强制转换解决方案可能会生成类转换exception。 发布可枚举扩展工作的人说这很尴尬。 我提出了一个只有一半尴尬的解决方案,不知道我是否会使用它:

 public static class UpTo { public static IEnumerable From(IEnumerable source) where F:T { // this cast is guaranteed to work return source.Select(f => (T) f); } } 

用法:

IEnumerable哺乳动物= UpTo <哺乳动物>。(kennel.Dogs)