如果没有明确的演员,这段代码如何运作?
static void Main()
{
IList<Dog> dogs = new List<Dog>();
IEnumerable<Animal> animals = dogs;
}
我在谈论这一行:
IEnumerable<animal> animals = dogs;
你可以在这里看到我能够在没有明确演员的情况下传递变量狗。但这怎么可能呢?为什么编译器允许我这样做?我不应该先这样做演员:
IEnumerable<Animal> animals = (List<Dog>) dogs;
代码将使用显式强制转换而不使用显式强制转换,但我无法理解为什么它允许我在没有显式强制转换的情况下将dog分配给animals引用变量。
答案 0 :(得分:8)
IEnumerable<T>
是covariant interface。这意味着,只要存在隐式引用转换或身份转换,就会有从IEnumerable<T1>
到IEnumerable<T2>
的隐式身份转换T1
到T2
。有关此术语的详细信息,请参阅documentation on conversions。
如果T2
是T1
的基类(直接或间接),或者是T1
实现的接口,则最常见的情况。如果T1
和T2
属于同一类型,或者T2
是dynamic
,也会出现这种情况。
在您的情况下,您正在使用IList<Dog>
和IList<T>
实施IEnumerable<T>
。这意味着任何IList<Dog>
也是IEnumerable<Dog>
,因此隐式参考转化为IEnumerable<Animal>
。
需要注意的一些重要事项:
只有接口和委托类型可以是协变的或逆变的。例如,List<T>
不是协变的,也不可能。例如,你不能写:
// Invalid
List<Animal> animals = new List<Dog>();
不所有接口都是协变的或可变的。例如,IList<T>
不是协变的,所以这也不是有效的:
// Invalid
IList<Animal> animals = new List<Dog>();
方差不适用于值类型,因此这不是有效的:
// Invalid
IEnumerable<object> objects = new List<int>();
只有在安全的情况下才允许通用差异:
当签名接受一个本身是逆变的参数时会有点混乱 - 即使参数通常是&#34;输入&#34;这个位置,它的逆转是逆转的。例如:
public interface Covariant<out T>
{
// This is valid, because T is in an output position here as
// Action<T> is contravariant in T
void Method(Action<T> input);
}
请务必阅读链接文档以获取更多信息!
答案 1 :(得分:5)
因为IEnumerable<T>
接口是Covariant。更多信息:
https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance
答案 2 :(得分:0)
答案 3 :(得分:-1)
虽然您没有显示,但我认为Dog
是Animal
的子类型。
您始终可以将子类型分配给其父类的实例。
所以List <Dog>
可以包含所有类型的狗,因此,如果您有Poodle
的课程,继承自Dog
,您可以将其放入狗的列表中。
因此IEumerable<Animal>
可以包含Dog
,Cat
和Poodle
个实例。
在这种情况下,它恰好只包含Dog
或Dog
的子类。
但你不能这样做:
var animals=new List <Animal>();
Ienumerable<Dog> dogs= animals;
即使您只将Dog
个实例插入动物身上。