52

我正在尝试将 A list of 传递DerivedClass给一个接受 的 list 的函数BaseClass,但出现错误:

cannot convert from 
'System.Collections.Generic.List<ConsoleApplication1.DerivedClass>' 
to 
'System.Collections.Generic.List<ConsoleApplication1.BaseClass>'

现在我可以将 myList<DerivedClass>转换为 a List<BaseClass>,但除非我理解为什么编译器不允许这样做,否则我会感到不舒服。

我发现的解释只是说它以某种方式违反了类型安全,但我没有看到它。谁能帮我吗?

List<DerivedClass>编译器允许从to转换的风险是什么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
{
}
4

6 回答 6

68

这是因为List<T>is in-variant, not co-variant,所以您应该更改为IEnumerable<T>which supports co-variant,它应该可以工作:

IEnumerable<BaseClass> bcl = new List<DerivedClass>();
public void doSomething(IEnumerable<BaseClass> bc)
{
    // do something with bc
}

关于泛型中的协变体的信息

于 2013-06-06T16:12:52.167 回答
56

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

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

AList<Mammal>无法转换为 aList<Animal>因为您可以将蜥蜴放入动物列表中。A List<Mammal> 不能转换为 a List<Giraffe>,因为列表中可能已经有老虎了

因此在 TList<T>中必须是不变的。

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

于 2013-06-06T16:20:43.453 回答
15

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

然而,对于像这样的可变类型List<T>,这从根本上是不安全的。

如果这是可能的,该方法将能够将 a 添加new OtherDerivedClass()到实际上只能保存 的列表中DerivedClass

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

于 2013-06-06T16:12:38.967 回答
2

当您有一个从基类派生的类时,这些类的任何容器都不会自动派生。因此,您不能只将 aList<Derived>转换为 a List<Base>

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

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

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

于 2013-06-06T16:15:57.280 回答
1

如果你能写

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

你可以打电话

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

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

于 2013-06-06T16:15:45.473 回答
0

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

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();
于 2015-09-24T18:28:49.800 回答