IList <T>和List <T>与接口的转换

Cur*_*tle 6 c# generics polymorphism interface

我一般都了解接口,继承和多态,但有一件事令我感到困惑.

在这个例子中,Cat实现了IAnimal,当然List实现了IList:

IList<IAnimal> cats = new List<Cat>();
Run Code Online (Sandbox Code Playgroud)

但它会生成编译错误(无法隐式转换类型...).如果我使用Cat继承的asbtract超类[Animal],它也将无效.但是,如果我用Cat替换IAnimal:

IList<Cat> cats = new List<Cat>();
Run Code Online (Sandbox Code Playgroud)

它编译得很好.

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

谁能解释为什么它无效?我确信这是一个合乎逻辑的解释.

Eri*_*ert 20

有一个合乎逻辑的解释,这个问题几乎每天都在StackOverflow上提出.

假设这是合法的:

IList<IAnimal> cats = new List<Cat>(); 
Run Code Online (Sandbox Code Playgroud)

是什么阻止了这种合法?

cats.Add(new Giraffe());
Run Code Online (Sandbox Code Playgroud)

没有."猫"是动物的名单,长颈鹿是动物,因此你可以在猫的名单中添加长颈鹿.

显然,这不是类型安全的.

在C#4中,我们添加了一个功能,如果元数据注释允许编译器证明它是类型安全的,那么你可以这样做.在C#4中,您可以这样做:

IEnumerable<IAnimal> cats = new List<Cat>(); 
Run Code Online (Sandbox Code Playgroud)

因为IEnumerable<IAnimal>没有Add方法,所以没有办法违反类型安全.

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

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

(从底部开始.)

  • @Mark:不客气,不用担心; 这个问题被提出这么多的事实是促使首先将功能添加到C#4的事情之一.显然,人们有一种直觉,认为通用差异应该是类型系统的一部分.现在只是教育人们关于什么样的方差可证明是安全的问题. (2认同)

Luk*_*keH 6

IList<T>出于类型安全原因,C#不支持这种差异.

如果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
Run Code Online (Sandbox Code Playgroud)

在C#4中,您可以执行以下操作:

IEnumerable<IAnimal> cats = new List<Cat>();
Run Code Online (Sandbox Code Playgroud)

这是因为IEnumerable<T>界面确实支持这种方差.An IEnumerable<T>是一个只读序列,因此您无法随后将a Dog或a 添加SquirrelIEnumerable<IAnimal>实际上是一个列表中Cat.