Cannot convert from List<DerivedClass> to List<Base
本文关键字:List lt to Base gt DerivedClass Cannot convert from | 更新日期: 2023-09-27 18:17:19
我试图将DerivedClass
的列表传递给需要BaseClass
列表的函数,但我得到错误:
cannot convert from
'System.Collections.Generic.List<ConsoleApplication1.DerivedClass>'
to
'System.Collections.Generic.List<ConsoleApplication1.BaseClass>'
现在我可以将我的List<DerivedClass>
转换为List<BaseClass>
,但我不觉得这样做很舒服,除非我明白为什么编译器不允许这样做。
我找到的解释只是说它违反了类型安全,但我没有看到它。有人能帮我吗?
编译器允许从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>
是不变,而不是协变,所以你应该改成支持协变的IEnumerable<T>
:
IEnumerable<BaseClass> bcl = new List<DerivedClass>();
public void doSomething(IEnumerable<BaseClass> bc)
{
// do something with bc
}
泛型协变信息
我找到的解释只是说它违反了类型安全,但我没有看到它。编译器允许从
List<DerivedClass>
转换为List<BaseClass>
的风险是什么?
这个问题几乎每天都被问到。
List<Mammal>
不能转换为List<Animal>
,因为可以将蜥蜴放入动物列表。List<Mammal
>不能转换为List<Giraffe>
,因为列表中可能已经有一只老虎。
因此List<T>
必须是不变的在t
然而,List<Mammal>
可以转换为IEnumerable<Animal>
(从c# 4.0开始),因为IEnumerable<Animal>
上没有添加蜥蜴的方法。IEnumerable<T>
在t中协变
你所描述的行为被称为协方差 & & dash;若A
为 B
,则List<A>
为 List<B>
。
但是,对于像List<T>
这样的可变类型,这从根本上是不安全的。
如果这是可能的,该方法将能够将new OtherDerivedClass()
添加到实际上只能容纳DerivedClass
的列表中。
协方差对不可变类型是安全的,尽管。net只在接口和委托中支持协方差。
如果您将List<T>
参数更改为IEnumerable<T>
,则可以
当您有一个从基类派生的类时,这些类的任何容器都不会自动派生。所以你不能直接将List<Derived>
转换为List<Base>
。
使用.Cast<T>()
创建一个新的列表,其中每个对象都被强制转换回基类:
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的实例到列表
我使用的解决方案是创建一个扩展类:
public static class myExtensionClass
{
public void doSomething<T>(List<BaseClass> bc) where T: BaseClass
{
// do something with bc
}
}
它使用泛型,但是当你调用它时,你不必指定类,因为你已经"告诉"编译器该类型与扩展类相同。
你可以这样称呼它:
List<DerivedClass> lst = new List<DerivedClass>();
lst.doSomething();