无法从List <DerivedClass>转换为List <BaseClass>
我想传递一个DerivedClass
的列表到一个函数,该函数需要一个BaseClass
的列表,但我得到的错误:
cannot convert from 'System.Collections.Generic.List<ConsoleApplication1.DerivedClass>' to 'System.Collections.Generic.List<ConsoleApplication1.BaseClass>'
现在我可以将我的List<DerivedClass>
转换为List<BaseClass>
,但我不觉得这样做,除非我明白为什么编译器不允许这样做。
我发现的解释只是简单地说它以某种方式违反了types安全,但是我没有看到它。 谁能帮我吗?
编译器允许从List<DerivedClass>
转换为List<BaseClass>
的风险是什么?
这是我的SSCCE:
class Program { public static void Main() { BaseClass bc = new DerivedClass(); // works fine List<BaseClass> bcl = new List<DerivedClass>(); // this line has an error doSomething(new List<DerivedClass>()); // this line has an error } public void doSomething(List<BaseClass> bc) { // do something with bc } } class BaseClass { } class DerivedClass : BaseClass { }
这是因为List<T>
是in-variant
,而不是co-variant
,因此您应该更改为IEnumerable<T>
,它支持co-variant
,它应该工作:
IEnumerable<BaseClass> bcl = new List<DerivedClass>(); public void doSomething(IEnumerable<BaseClass> bc) { // do something with bc }
有关共同变体的信息
我发现的解释只是简单地说它以某种方式违反了types安全,但是我没有看到它。 编译器允许从
List<DerivedClass>
转换为List<BaseClass>
的风险是什么?
这个问题几乎每天都被问到。
List<Mammal>
不能转换为List<Animal>
因为你可以把一只蜥蜴放入动物列表 。 List<Mammal
>不能转换为List<Giraffe>
因为List<Giraffe>
可能已经有一只老虎了 。
因此, List<T>
在T中必须是不变的。
但是, List<Mammal>
可以转换为IEnumerable<Animal>
(从C#4.0开始),因为IEnumerable<Animal>
没有添加蜥蜴的方法。 IEnumerable<T>
在T中是协变的。
你所描述的行为叫做协变 – 如果A
是 B
,那么List<A>
是 List<B>
。
但是,像List<T>
这样的可变types,基本上是不安全的。
如果这是可能的,该方法将能够添加一个new OtherDerivedClass()
到一个实际上只能容纳DerivedClass
的列表中。
协变对于不可变types是安全的,尽pipe.Net只在接口和委托中支持它。
如果将List<T>
参数更改为IEnumerable<T>
,则可以使用
当你有一个从基类派生的类时,这些类的任何容器都不会自动派生。 所以你不能只把List<Derived>
转换成List<Base>
。
使用.Cast<T>()
创build一个新的列表,其中每个对象都被转换回基类:
List<MyDerived> list1 = new List<MyDerived>(); List<MyBase> list2 = list1.Cast<MyBase>().ToList();
请注意,这是一个新的列表,而不是原始列表的转换版本,因此这个新列表中的操作不会反映在原始列表中。 包含对象的操作将反映,但是。
如果你可以写
List<BaseClass> bcl = new List<DerivedClass>();
你可以打电话
var instance = new AnotherClassInheritingFromBaseClass(); bc1.Add(instance);
将不是DerivedClass的实例添加到列表中。
我使用的解决scheme是创build一个扩展类:
public static class myExtensionClass { public void doSomething<T>(List<BaseClass> bc) where T: BaseClass { // do something with bc } }
它使用generics,但是当你调用它时,你不必指定类,因为你已经“告诉”了编译器的types与扩展类相同。
你可以这样调用它:
List<DerivedClass> lst = new List<DerivedClass>(); lst.doSomething();