简体   繁体   English

无法从列表转换<DerivedClass>列出<BaseClass>

[英]Cannot convert from List<DerivedClass> to List<BaseClass>

I'm trying to pass A list of DerivedClass to a function that takes a list of BaseClass , but I get the error:我试图将DerivedClass列表DerivedClass给一个接受BaseClass列表的函数,但出现错误:

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

Now I could cast my List<DerivedClass> to a List<BaseClass> , but I don't feel comfortable doing that unless I understand why the compiler doesn't allow this.现在我可以将我的List<DerivedClass>转换为List<BaseClass> ,但我不喜欢这样做,除非我明白为什么编译器不允许这样做。

Explanations that I have found have simply said that it violates type safety somehow, but I'm not seeing it.我发现的解释只是说它以某种方式违反了类型安全,但我没有看到。 Can anyone help me out?谁能帮我吗?

What is the risk of the compiler allowing conversion from List<DerivedClass> to List<BaseClass> ?编译器允许从List<DerivedClass>转换为List<BaseClass>的风险是什么?


Here's my SSCCE:这是我的 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
{
}

It is because List<T> is in-variant , not co-variant , so you should change to IEnumerable<T> which supports co-variant , it should work:这是因为List<T>in-variant ,而不是co-variant ,所以您应该更改为支持co-variant IEnumerable<T> ,它应该可以工作:

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

Information about co-variant in generic关于泛型中的协变体的信息

Explanations that I have found have simply said that it violates type safety somehow, but I'm not seeing it.我发现的解释只是说它以某种方式违反了类型安全,但我没有看到。 What is the risk of the compiler allowing conversion from List<DerivedClass> to List<BaseClass> ?编译器允许从List<DerivedClass>转换为List<BaseClass>的风险是什么?

This question is asked almost every day.这个问题几乎每天都会被问到。

A List<Mammal> cannot be converted to a List<Animal> because you can put a lizard into a list of animals . List<Mammal>不能转换为List<Animal>因为你可以把蜥蜴放入一个动物列表中 A List<Mammal > cannot be converted to a List<Giraffe> because there might be a tiger in the list already . List<Mammal > 不能转换为List<Giraffe>因为List<Giraffe>可能已经有老虎了

Therefore List<T> has to be invariant in T.因此List<T>在 T 中必须是不变的。

However, List<Mammal> can be converted to IEnumerable<Animal> (as of C# 4.0) because there is no method on IEnumerable<Animal> that adds a lizard.但是, List<Mammal>可以转换为IEnumerable<Animal> (从 C# 4.0 开始),因为IEnumerable<Animal>没有添加蜥蜴的方法。 IEnumerable<T> is covariant in T. IEnumerable<T>在 T 中是协变的。

The behavior you're describing is called covariance – if A is B , then List<A> is List<B> .您所描述的行为称为协方差- 如果AB ,则List<A>List<B>

However, for mutable types like List<T> , that is fundamentally unsafe.但是,对于像List<T>这样的可变类型,这从根本上是不安全的。

Had this been possible, the method would be able to add a new OtherDerivedClass() to a list that can actually only hold DerivedClass .如果这是可能的,该方法将能够将一个new OtherDerivedClass()添加到一个实际上只能DerivedClass的列表中。

Covariance is safe on immutable types, although .Net only supports it in interfaces and delegates.协方差在不可变类型上是安全的,尽管 .Net 仅在接口和委托中支持它。
If you change the List<T> parameter to IEnumerable<T> , that will work如果您将List<T>参数更改为IEnumerable<T> ,那将起作用

When you have a class derived from a base class, any containers of those classes are not automatically derived.当您有一个从基类派生的类时,这些类的任何容器都不会自动派生。 So you cannot just cast a List<Derived> to a List<Base> .所以你不能只是将List<Derived>转换为List<Base>

Use .Cast<T>() to create a new list where each object is cast back to the base class:使用.Cast<T>()创建一个新列表,其中每个对象都被.Cast<T>()回基类:

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

Note that this is a new list, not a cast version of your original list, so operations on this new list will not reflect on the original list.请注意,这是一个新列表,而不是原始列表的演员表,因此对这个新列表的操作不会反映在原始列表上。 Operations on the contained objects will reflect, however.但是,对所包含对象的操作将反映出来。

If you could write如果你能写

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

you could call你可以打电话

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

Adding an instance which is not a DerivedClass to the list.将不是 DerivedClass 的实例添加到列表中。

A solution i used was to create an extension class:我使用的一个解决方案是创建一个扩展类:

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

It uses generic, but when you call it you don't have to especify the class since you already 'told' the compiler the type is the same of extended class.它使用泛型,但是当您调用它时,您不必指定类,因为您已经“告诉”编译器该类型与扩展类相同。

You would call it this way:你会这样称呼它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM