9

给定以下类型:

public interface IPrimary{ void doBattle(); }

// an ISecondary "is" an IPrimary
public interface ISecondary : IPrimary {  }

// An implementation of ISecondary is also an IPrimary:
internal class SecondaryImpl : ISecondary
{
    // Required, since this is an IPrimary
    public void doBattle(){ }
}

为什么我不能这样做?

List<IPrimary> list = new List<ISecondary>();

这会导致以下编译错误:

参数类型“System.Collections.Generic.List”不可分配给参数类型“System.Collections.Generic.List”

我理解错误,并且我意识到有解决方法。我只是看不出为什么不允许这种直接转换的任何明确原因。ISecondary毕竟,列表中包含的值应该是(通过扩展)类型的值。IPrimary那么为什么会List<IPrimary>List<ISecondary>解释为不相关的类型呢?

任何人都可以清楚地解释以这种方式设计 C# 的原因吗?

一个稍微扩展的示例:我在尝试执行类似于以下操作时遇到了这个问题:

internal class Program
{
    private static void Main(string[] args)
    {
        // Instance of ISecondary, and by extention, IPrimary:
        var mySecondaryInstance = new SecondaryImpl();

        // This works as expected:
        AcceptImpl(mySecondaryInstance);

        // List of instances of ISecondary, which are also, 
        // by extention, instances of IPrimary:
        var myListOfSecondaries = new List<ISecondary> {mySecondaryInstance};

        // This, however, does not work (results in a compilation error):
        AcceptList(myListOfSecondaries);
    }

    // Note: IPrimary parameter:
    public static void AcceptImpl(IPrimary instance){  }

    // Note: List of type IPrimary:
    public static void AcceptList(List<IPrimary> list){  }

}
4

4 回答 4

11
public class Animal
{
    ...
}

public class Cat: Animal
{
    public void Meow(){...}
}

List<Cat> cats = new List<Cat>();

cats.Add(new Cat());

cats[0].Meow();  // Fine.

List<Animal> animals = cats; // Pretend this compiles.

animals.Add(new Animal()); // Also adds an Animal to the cats list, since animals references cats.

cats[1].Meow(); // cats[1] is an Animal, so this explodes!

这就是为什么。

于 2013-01-31T08:53:43.513 回答
8

为什么我不能这样做?List<IPrimary> list = new List<ISecondary>();

想象一下,你有一个这样定义的方法:

public void PopulateList(List<IPrimary> listToPopulate)
{
    listToPopulate.Add(new Primary());  // Primary does not implement ISecondary!
}

如果你将它List<ISecondary>作为参数传递会发生什么?

List<ISecondary>无法分配List<IPrimary>的错误是编译器使您摆脱此类麻烦的方式。

于 2013-01-31T08:50:57.063 回答
5
class Evil : IPrimary {...}
list.Add(new Evil()); // valid c#, but wouldn't work

它可以保护您免受错误的影响。列表实例(对象)需要辅助实例。并非每个主要都是次要的。然而期望是一个 list-of-primary 可以包含任何 primary。如果我们可以将二级列表视为主要列表:坏事。

实际上,数组确实允许这样做 - 如果您弄错了,则会在运行时出错。

于 2013-01-31T08:50:24.183 回答
1

列表类型在它们的泛型参数中不是协变的(即List<ISecondary>不是的子类型)的List<IPrimary>原因是它们是可读写的。在您的扩展示例中,您的方法AcceptList可以执行list.Add(x)where xis anIPrimary但不是ISecondary.

请注意,这IEnumerable<T>是正确的协变,而数组是协变类型的(您可以执行上面尝试的操作),但出于同样的原因,这不是正确的 - 将元素添加到集合中将在运行时失败。

于 2013-01-31T08:51:50.843 回答