无法从List <derivedclass>转换为List <baseclass> </baseclass> </derivedclass>

时间:2013-06-06 16:08:27

标签: c#

我正在尝试将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
{
}

6 个答案:

答案 0 :(得分:45)

这是因为List<T>in-variant,而不是co-variant,因此您应该更改为支持IEnumerable<T>的{​​{1}},它应该有效:

co-variant

有关co-variant in generic

的信息

答案 1 :(得分:43)

  

我发现的解释只是说它以某种方式违反了类型安全,但我没有看到它。编译器允许从List<DerivedClass>转换为List<BaseClass>的风险是什么?

几乎每天都会问这个问题。

List<Mammal>无法转换为List<Animal>因为您可以将蜥蜴放入动物列表中List<Mammal&gt;无法转换为List<Giraffe>,因为列表中可能已有老虎

因此,List<T>必须在T中不变

但是,List<Mammal>可以转换为IEnumerable<Animal>(从C#4.0开始),因为IEnumerable<Animal>上没有添加蜥蜴的方法。 IEnumerable<T>在T.

中是协变

答案 2 :(得分:12)

您所描述的行为称为协方差 - 如果A B,那么List<A> List<B>

但是,对于像List<T>这样的可变类型,这基本上是不安全的。

如果可行,该方法可以将new OtherDerivedClass()添加到实际上只能保留DerivedClass的列表。

协方差在不可变类型上是安全的,尽管.Net仅在接口和委托中支持它 如果您将List<T>参数更改为IEnumerable<T>,则可以使用

答案 3 :(得分:2)

如果有一个派生自基类的类,则不会自动派生这些类的任何容器。因此,您不能只将List<Derived>投射到List<Base>

使用.Cast<T>()创建一个新列表,其中每个对象都被强制转换回基类:

List<MyDerived> list1 = new List<MyDerived>();
List<MyBase> list2 = list1.Cast<MyBase>().ToList();

请注意,这是一个新列表,而不是原始列表的演员版本,因此对此新列表的操作不会反映在原始列表中。但是,对所包含对象的操作将反映出来。

答案 4 :(得分:1)

如果你可以写

List<BaseClass> bcl = new List<DerivedClass>(); 

你可以打电话

var instance = new AnotherClassInheritingFromBaseClass();
bc1.Add(instance);

将不是DerivedClass的实例添加到列表中。

答案 5 :(得分:0)

我使用的解决方案是创建扩展类:

public static class myExtensionClass 
{
    public void doSomething<T>(List<BaseClass> bc) where T: BaseClass
    {
        // do something with bc
    }
}

它使用泛型,但是当你打电话给它时,你不必指定课程,因为你已经告诉过#c;编译器的类型与扩展类相同。

你会这样称呼它:

List<DerivedClass> lst = new List<DerivedClass>();
lst.doSomething();