C# IList<;T>;和列表<;T>;带接口的转换
我通常理解接口、继承和多态性,但有一件事让我感到困惑 在本例中,Cat实现了IAnimal,当然List实现了IList: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],它也不会起作用。但是,如果
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将typeT
作为参数,并将其作为方法的返回值返回,这使得协方差问题重重
例如,如果在您的示例中:
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》中关于这一点的部分,该部分也提供了一个很好的总结(以及处理协方差和逆变的策略)。在我看来,任何与这一概念作斗争的人都不应该再看这两种资源了。感谢所有提供指导的人。