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
来思考。 您还可以使用Cast
或OfType
与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)