Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# IList<;T>;和列表<;T>;带接口的转换_C#_Generics_Interface_Polymorphism - Fatal编程技术网

C# IList<;T>;和列表<;T>;带接口的转换

C# IList<;T>;和列表<;T>;带接口的转换,c#,generics,interface,polymorphism,C#,Generics,Interface,Polymorphism,我通常理解接口、继承和多态性,但有一件事让我感到困惑 在本例中,Cat实现了IAnimal,当然List实现了IList: IList<IAnimal> cats = new List<Cat>(); IList<Cat> cats = new List<Cat>(); IList cats=new List(); 但它会生成编译错误(无法隐式转换类型…)。如果我使用Cat继承的asbtract超类[Animal],它也不会起作用。但是,如果

我通常理解接口、继承和多态性,但有一件事让我感到困惑

在本例中,Cat实现了IAnimal,当然List实现了IList

IList<IAnimal> cats = new List<Cat>();
IList<Cat> cats = new List<Cat>();
IList cats=new List();
但它会生成编译错误(无法隐式转换类型…)。如果我使用Cat继承的asbtract超类[Animal],它也不会起作用。但是,如果我将IAnimal替换为Cat:

IList<IAnimal> cats = new List<Cat>();
IList<Cat> cats = new List<Cat>();
IList cats=new List();
它编译得很好

在我看来,因为Cat实现了IAnimal,所以第一个示例应该是可以接受的,它允许我们为列表和包含的类型返回一个接口


有人能解释为什么它无效吗?我相信有一个合乎逻辑的解释。

这种类型的功能在C#4.0中不受支持。期望您想要的行为是合理的,只是目前不受支持。

您可以使用LINQ实现这一点:

IList<IAnimal> cats = new List<Cat>().Cast<IAnimal>();
IList cats=new List().Cast();
出于类型安全原因,
C#不支持IList上的此类差异

如果C#真的支持这一点,你希望这里会发生什么

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

cats.Add(new Dog());         // a dog is an IAnimal too
cats.Add(new Squirrel());    // and so is a squirrel
IList cats=new List();
cats.Add(新狗());//狗也是动物
cats.Add(新松鼠());//松鼠也是
在C#4中,您可以执行以下操作:

IEnumerable<IAnimal> cats = new List<Cat>();
IEnumerable猫=新列表();

这是因为
IEnumerable
接口确实支持这种变化。一个
IEnumerable
是一个只读序列,因此你不可能在
IEnumerable
中添加一只
Dog
或一只
Squirrel
,这实际上是一个
Cat
的列表,这是一个合乎逻辑的解释,而这个确切的问题几乎每天都会在StackOverflow上被问到

假设这是合法的:

IList<IAnimal> cats = new List<Cat>(); 
没什么。“猫”是一个动物列表,而长颈鹿是一种动物,因此您可以将长颈鹿添加到猫列表中

显然,这是不安全的

在C#4中,我们添加了一个特性,如果元数据注释允许编译器证明它是类型安全的,则可以这样做。在C#4中,您可以执行以下操作:

IEnumerable<IAnimal> cats = new List<Cat>(); 
IEnumerable猫=新列表();
因为
IEnumerable
没有Add方法,所以无法违反类型安全性

有关更多详细信息,请参阅我关于如何在C#4中设计此功能的系列文章

(从底部开始。)

IList
不是协变接口(或者是
IList
)。这是因为IList将type
T
作为参数,并将其作为方法的返回值返回,这使得协方差问题重重

例如,如果在您的示例中:

IList cats=new List()

您想添加一个新的猫,从猫,它将允许:

cats.Add(newdog())


假设Dog也实现了IAnimal,这显然是不正确的,不会起作用。这就是为什么IList不是协变或逆变接口。

如果您需要在类似列表的接口上使用协变和逆变,您应该定义接口IReadableList和IWritableList,并从列表中派生一个实现这两个ReadableList的类型。您能澄清一下吗?C#4.0支持协变和逆变,但IList不是协变接口。哈哈,Eric Lippert刚刚回答了这个问题,所以我想这就是总结:DThanks Eric。我一定会读你的文章。也为不断重复的问题道歉。。。下次会做得更好。@Mark:不客气,不用担心;这个问题被问得如此之多,这一事实是促使我们首先向C#4添加该特性的原因之一。显然,人们有一种直觉,即泛型变异应该是类型系统的一部分。现在只需要教育人们什么样的变异是可证明安全的。更新。我现在读了Eric的文章,他们完美地澄清了协变和逆变。我还阅读了Jon Skeet在《C#Depth》中关于这一点的部分,该部分也提供了一个很好的总结(以及处理协方差和逆变的策略)。在我看来,任何与这一概念作斗争的人都不应该再看这两种资源了。感谢所有提供指导的人。