无法从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();