无法从List <DerivedClass>转换为List <BaseClass>

Sam*_*ica 34 c#

我正在尝试将一个列表传递给一个列表的DerivedClass函数BaseClass,但是我得到了错误:

cannot convert from 
'System.Collections.Generic.List<ConsoleApplication1.DerivedClass>' 
to 
'System.Collections.Generic.List<ConsoleApplication1.BaseClass>'
Run Code Online (Sandbox Code Playgroud)

现在我可以把我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
{
}
Run Code Online (Sandbox Code Playgroud)

cuo*_*gle 49

这是因为List<T>in-variant,不是co-variant,所以你应该更改为IEnumerable<T>支持co-variant,它应该工作:

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

关于通用的共变体的信息

  • 从.Net 4.5开始,如果方法需要随机访问集合元素,还可以使用“IReadOnlyList&lt;T&gt;”。 (4认同)

Eri*_*ert 44

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

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

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

因此List<T>必须在T中保持不变.

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

  • +1一个简单的动物学解释,解释了这一切.(在IEnumerable <Animal>上没有添加蜥蜴的方法 - >我会检查以防万一;)) (5认同)
  • @MGOwen:你有一份哺乳动物名单.您决定将其视为动物清单.你把一只蜥蜴放入动物名单.**但它确实是一份哺乳动物名单**.现在你已经列出了一只带有蜥蜴的哺乳动物,违反了一系列哺乳动物只有哺乳动物的预期.结论违反了类型系统,因此其中一个步骤必须是非法的.这是第一个:你不能把一份哺乳动物列为动物名单. (4认同)
  • 我感到愚蠢……长颈鹿似乎很明显,但是为什么不能够将哺乳动物传递给可以处理动物的东西,仅仅是因为它也可以处理非哺乳动物呢? (3认同)
  • @RyanPergent:“mammals.Cast&lt;Animal&gt;().ToList()”将是一个新的“List&lt;Animal&gt;”,其内容与“mammals”相同,但转换需要构建列表的副本,因此速度很慢。如果优化器很聪明,那么引用转换实际上根本不需要时间,因为除了引用的解释方式之外,引用转换*什么也没有改变。 (3认同)
  • @MichaelRodrigues 很好的问题。请注意,IEnumerable&lt;T&gt; 只允许您“读取”序列。它不允许你“写”它。这就是为什么它可以保证协方差的安全。由于无法*通过“IE&lt;T&gt;”将老虎“插入”到“IE&lt;Animal&gt;”中,因此它是否“真的”是“IE&lt;Giraffe&gt;”并不重要。 (3认同)
  • @RyanPergent:这不是一个新列表。这是同一个列表。`List&lt;T&gt;` 是一个*引用类型*;引用转换*重新解释引用的含义*。它不会改变引用引用的对象。 (2认同)

SLa*_*aks 13

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

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

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

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