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
{
}

Cannot convert from List<DerivedClass> to List<Base

这是因为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();