-
所谓逆变和协变,是.net 为了把面向对象的多态特性中的里氏转换 搬到泛型中依旧可以使用.
-
现有以下类:
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(); } }
-
根据面向对象多态特性,父类类型的变量可以指向子类类型的对象,那么下面这段代码按照理论来讲应该没有问题,可是编译器报错了:"无法将类型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(); } }
注意:协变的泛型类型只能作用在接口或者委托上,并且协变的泛型类型只能用于接口方法或者委托的返回值
逆变:
-
现有一个委托:
public delegate void TestDel<T>(T obj);
-
乍一看貌似有点不和常理,子类类型变量指向父类类型对象。如果换一种思考方式,这个委托是要传一个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(); }
========================================结束线=========================================================