C#中的协变逆变

75 阅读1分钟

一、C#中的协变

众所周知,子类对象可以赋值给父类,例如 string str = string.Empty; object obj = str; 不会报错。如果换一种方式,代码如下:

public class Father
{
    private string Name { get; set; }
}

public class Son : Father
{
    private string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        List<Father> fathers = new List<Son>();      
    }
}

很显然List fathers = new List();这句会报错,因为虽然Son和Father之间存在继承关系,但是两个List之间却不存在继承关系。 因此在C#4.0中引入协变的概念,使用协变,可以看到List实现了IEnumerable接口,而IEnumerable接口的定义:

namespace System.Collections.Generic
{
    //
    // 摘要:
    //     公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代。
    //
    // 类型参数:
    //   T:
    //     要枚举的对象的类型。
    public interface IEnumerable<out T> : IEnumerable
    {
        //
        // 摘要:
        //     返回一个循环访问集合的枚举器。
        //
        // 返回结果:
        //     用于循环访问集合的枚举数。
        IEnumerator<T> GetEnumerator();
    }
}

在泛型T前面有一个out修饰符来修饰这个泛型,这时改变之前的写法:

IEnumerable<Father> fathers = new List<Son>();

发现不再报错,这就是协变。

二、C#中的逆变

逆变的关键字修饰符是in,这里我们自定义一个逆变:

 public interface IPerson<in T>
{

}

public class Person<T> : IPerson<T>
{

}

public class Father
{
    private string Name { get; set; }

}

public class Son : Father
{
    private string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        IPerson<Son> person = new Person<Father>();//逆变
    }
}

这样,父类Father泛型类型的对象可以赋值给子类泛型类型,这就是C#中的逆变。