怎么理解逆变和协变?

逆变和协变

  • 所谓逆变和协变,是.net 为了把面向对象的多态特性中的里氏转换 搬到泛型中依旧可以使用.

协变:

  1. 现有以下类:

     public class Person
    {
        public long Id { get; set; }
        public string Name { get; set; }
    }
    public class Chinese:Person
    {
    ​
    }
    ​
    ​
    ​
    public interface XieBian<T>
    {
        T Print();
    }
    public class XieBianInstance<T> : XieBian<T>
    {
        public T Print()
        {
            throw new NotImplementedException();
        }
    }
  2. 根据面向对象多态特性,父类类型的变量可以指向子类类型的对象,那么下面这段代码按照理论来讲应该没有问题,可是编译器报错了:"无法将类型Chinese转换成Person"。原来泛型不支持。

    XieBian<Person> xie = new XieBianInstance<Chinese>();

    如果想这么使用,那就得使用协变,其实就是在接口或者委托的泛型上加上out关键字

    public interface XieBian<out T>
    {
        T Print();
    }
    public class XieBianInstance<T> : XieBian<T>
    {
        public T Print()
        {
            throw new NotImplementedException();
        }
    }

  注意:协变的泛型类型只能作用在接口或者委托上,并且协变的泛型类型只能用于接口方法或者委托的返回值

逆变:

  1. 现有一个委托:

    public delegate void TestDel<T>(T obj);
  2. 乍一看貌似有点不和常理,子类类型变量指向父类类型对象。如果换一种思考方式,这个委托是要传一个Chinese类型的对象进去,因为Chinese是继承Person的,根据里氏转换可得,这么写也没问题。可问题还是报错了:"无法将类型Person转换成Chinese"。泛型是不支持这么做得。

    TestDel<Chinese> testDel = new TestDel<Person>((ch)=> { }); 

    如果想这么做,就必须利用逆变,其实就是在上面得委托泛型中加上int关键字

    public delegate void TestDel<in T>(T obj);

    改成这样,上面的代码就可以正常执行了。

  注意:逆变得泛型类型只能作用在接口和委托上,并且逆变得泛型类型只能用于接口方法或者委托的参数。不可用于返回值等等。

 

 逆变的接口示例:

  

public interface NiBian<in T>
    {
        void Print(T obj);
    }
    public class NiBianInstance<T> : NiBian<T>
    {

        public void Print(T obj)
        {
            Console.WriteLine(obj.GetType().Name);
        }
    }
 static void Main(string[] args)
        {
            NiBian<Chinese> niBian = new NiBianInstance<Person>();

            Console.ReadKey();
        }

========================================结束线=========================================================

 

 

 

原文地址:https://www.cnblogs.com/norain/p/InversionAndCovariance.html