如果没有明确的演员,这段代码如何运作?
static void Main()
{
IList<Dog> dogs = new List<Dog>();
IEnumerable<Animal> animals = dogs;
}
Run Code Online (Sandbox Code Playgroud)
我在谈论这一行:
IEnumerable<animal> animals = dogs;
Run Code Online (Sandbox Code Playgroud)
你可以在这里看到我能够在没有明确演员的情况下传递变量狗.但这怎么可能呢?为什么编译器允许我这样做?我不应该先做这样的演员:
IEnumerable<Animal> animals = (List<Dog>) dogs;
Run Code Online (Sandbox Code Playgroud)
代码将使用显式强制转换而不使用显式强制转换但我无法理解为什么它允许我在没有显式强制转换的情况下将狗分配给animals引用变量.
IEnumerable<T>是一个协变的接口.这意味着有一个由隐式标识转换IEnumerable<T1>到IEnumerable<T2>只要有一个隐式引用转换或标识转换从T1到T2.有关此术语的更多详细信息,请参阅有关转换的文档.
如果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>();
Run Code Online (Sandbox Code Playgroud)并非所有接口都是协变的或可变的.例如,IList<T>不是协变的,所以这也是无效的:
// Invalid
IList<Animal> animals = new List<Dog>();
Run Code Online (Sandbox Code Playgroud)方差不适用于值类型,因此无效:
// Invalid
IEnumerable<object> objects = new List<int>();
Run Code Online (Sandbox Code Playgroud)只有在安全的情况下才允许通用差异:
当签名接受一个本身是逆变的参数时,它会有点混乱 - 即使一个参数通常是一个"输入"位置,它也会因逆变而逆转.例如:
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);
}
Run Code Online (Sandbox Code Playgroud)
绝对阅读链接文档以获取更多信息!
这是因为IEnumerable<T>界面是协变的.更多资讯:
https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance