泛型类型的协变(covariant)和逆变

时间:2023-02-13 22:20:19

官网:http://msdn.microsoft.com/zh-cn/library/dd799517.aspx

原文链接:http://book.51cto.com/art/201112/308570.htm

参考链接:http://www.cnblogs.com/yukaizhao/archive/2011/10/27/xiebian-nibian.html

1.3.4  泛型类型的协变(covariant)和逆变(contravariant)

在.NET 4.0之前的版本中,泛型类型是不支持协变和逆变的,但是委托类型的参数是支持协变和逆变的。什么是协变和逆变呢?在编程语言中,“协变”是指能够使用与原始指定的派生类型相比派生程度更大的类型;“逆变”则是指能够使用派生程度更小的类型。

下面的代码很好地演示了委托类型的协变。假定有一个类Animals,从其派生出一个子类Dogs,那么当定义一个委托,该委托返回Animals。用户也可以将一个返回Dogs的委托赋值给该委托,称之为协变,见代码1.4。

代码1.4  委托的协变

  1. class Program
  2. {
  3. public delegate Animals HandlerMethod();    //返回Animals的委托
  4. public static Animals FirstHandler()        //返回Animals的方法实现
  5. {
  6. Console.WriteLine("返回Animals的委托");
  7. return null;
  8. }
  9. public static Dogs Secondhandler()          //返回Dogs的方法实现
  10. {
  11. Console.WriteLine("返回Dogs的委托");
  12. return null;
  13. }
  14. static void Main(string[] args)
  15. {
  16. HandlerMethod handler1 = FirstHandler;  //标准委托
  17. HandlerMethod handler2 = Secondhandler; //委托协变
  18. }
  19. }
  20. // 定义一个Animals的类
  21. public class Animals
  22. {
  23. public string  Location { get; set; }
  24. }
  25. // 定义一个派生自Animals的Dogs类
  26. public class Dogs : Animals
  27. {
  28. public string Cry { get; set; }
  29. }

在上面的代码中,首先定义了Animals类和Dogs类,然后定义了一个名为HandlerMethod的委托,该委托返回Animals类型的 值。在Main()方法中,分别赋给一个返回Animals类型的值和一个返回Dogs类型值的方法。可以看到,由于委托的协变特性,使得本来返回一个 Animals的委托可以接受一个返回Dogs的委托。

.NET 4.0引入了in/out参数,使泛型类型的协变和逆变得以实现。比如定义一个泛型接口或者是泛型委托,可以使用out关键字,将泛型类型参数声明为协变。协变类型必须满足条件:类型仅用作接口方法的返回类型,不用作方法参数的类型。

可以使用in关键字,将泛型类型参数声明为逆变。逆变类型只能用作方法参数的类型,不能用作接口方法的返回类型。逆变类型还可用于泛型约束。下面的示例演示了如何使用in/out参数来设置泛型类型的协变和逆变。协变的使用见代码1.5。

代码1.5  泛型的协变

  1. interface ITest<out T>                  //定义一个支持协变的接口
  2. {
  3. T X { get; }                            //属性
  4. T M();                                  //返回T类型的方法
  5. }
  6. //定义一个实现接口的泛型类
  7. class TestClass<T> : ITest<T>
  8. where T : Base, new()                 //约束T要派生自Base,具有构造函数
  9. {
  10. public T X { get; set; }
  11. //实现泛型方法
  12. public T M()
  13. {
  14. return new T();
  15. }
  16. }
  17. //定义两个类
  18. class Base { }
  19. class Derived : Base { }
  20. class Program
  21. {
  22. static void Main(string[] args)
  23. {
  24. ITest<Derived> _derived =
  25. new TestClass<Derived> { X = new Derived() };                                       //使用对象初始化语法赋初值
  26. ITest<Base> _base = _derived;   //泛型协变
  27. Base x = _base.X;
  28. Base m = _base.M();
  29. }
  30. }

在上面的代码中,定义了一个泛型接口ITest,注意使用了out参数以支持协变。然后TestClass泛型类实现了接口,并且定义了泛型约束指 定T类型必须是派生自Base类的子类。可以看到在Main主窗体中,定义了一个ITest的接口,然后利用泛型的协变特性来进行泛型类型之间的变换。

与协变相反的是,逆变是将基类转换为派生类,泛型逆变有如下两条规则:

泛型参数受in关键字约束,只能用于属性设置或委托(方法)参数。

隐式转换目标的泛型参数类型必须是当前类型的“继承类”。

例如,代码1.6定义了一个接口,演示了哪些是允许协变,哪些是允许逆变的。

代码1.6  接口的逆变

  1. interface ITest<in T>
  2. {
  3. T X
  4. {
  5. get;    //获取属性不允许逆变
  6. set;    //设置属性允许逆变!
  7. }
  8. T M(T o);   //只允许方法参数,不能作用于方法返回值
  9. }

与协变相反,逆变符合多态性的规律,逆变有些令人费解,不过逆变主要是为泛型委托准备的。逆变的使用如代码1.7所示。

代码1.7  委托的逆变

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Action<Base> _base = (o) => Console.WriteLine(o);//定义一个Base基类
  6. Action<Derived> _derived = _base;       //使用协变将基类转换为派生类
  7. _derived(new Derived());                    //逆变的效果
  8. }
  9. }

以上代码中创建了一个委托,是基于Base类,但是在后面的赋值语句中,将基类赋给派生类,形成了逆变。

泛型类型的协变(covariant)和逆变的更多相关文章

  1. 协变&lpar;covariant&rpar;和逆变&lpar;contravariant&rpar;

    我们知道子类转换到父类,在C#中是能够隐式转换的.这种子类到父类的转换就是协变. 而另外一种类似于父类转向子类的变换,可以简单的理解为“逆变”. 上面对逆变的简单理解有些牵强,因为协变和逆变只能针对接 ...

  2. C&num;中的协变&lpar;Covariance&rpar;和逆变&lpar;Contravariance&rpar;

    摘要 ● 协变和逆变的定义是什么?给我们带来了什么便利?如何应用? ● 对于可变的泛型接口,为什么要区分成协变的和逆变的两种?只要一种不是更方便吗? ● 为什么还有不可变的泛型接口,为什么有的泛型接口 ...

  3. C&num;中的协变OUT和逆变

    泛型接口和泛型委托中经常使用可变性 in  逆变,out  协变 从 list<string>转到list<object> 称为协变 (string 从object 派生,那么 ...

  4. C&num; 协变out 、逆变 in

    需求:泛型使用多态性 备注:协变逆变只能修饰 接口和委托 简单理解: 1.使用 in 修饰后为逆变,只能用作形参使用 ,参考 public delegate void Action<in T&g ...

  5. Scala 基础(十六):泛型、类型约束-上界&lpar;Upper Bounds&rpar;&sol;下界&lpar;lower bounds&rpar;、视图界定&lpar;View bounds&rpar;、上下文界定&lpar;Context bounds&rpar;、协变、逆变和不变

    1 泛型 1)如果我们要求函数的参数可以接受任意类型.可以使用泛型,这个类型可以代表任意的数据类型. 2)例如 List,在创建 List 时,可以传入整型.字符串.浮点数等等任意类型.那是因为 Li ...

  6. Kotlin泛型与协变及逆变原理剖析

    在上一次https://www.cnblogs.com/webor2006/p/11234941.html中学习了数据类[data class]相关的知识,这次会学习关于泛型相关的东东,其中有关于泛型 ...

  7. &period;NET泛型03&comma;泛型类型的转换&comma;协变和逆变

    协变(Convariant)和逆变(Contravariant)的出现,使数组.委托.泛型类型的隐式转换变得可能. 子类转换成基类,称之为协变:基类转换成子类,称之为逆变..NET4.0以来,支持了泛 ...

  8. 深入理解 C&num; 协变和逆变

    msdn 解释如下: “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型. “逆变”则是指能够使用派生程度更小的类型. 解释的很正确,大致就是这样,不过不够直白. 直白的理解: “协变” ...

  9. 【温故而知新-万花筒】C&num; 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程

    额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...

随机推荐

  1. ios xcode Code signing failed 解决方案

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #454545; min ...

  2. POJ 3468 A Simple Problem with Integers

    线段树的指针表示法. 代码还有待消化.. 代码里面多次用到了函数递归,感觉这次对递归又有了深一层的理解. #define LOCAL #include <iostream> #includ ...

  3. scala学习笔记-集合

    变长数组:数组缓冲 Scala中对于那种长度会变的数组的数据结构为ArrayBuffer. import scala.collection.mutable.ArrayBuffer; // 一个空的数组 ...

  4. &lbrack;Android&rsqb;Plug-in com&period;android&period;ide&period;eclipse&period;adt was unable to load class com&period;android&period;ide

    今天启动eclipse的时候报了上述错误,打开xml是都报错.其实解决方法很简单:重启eclipse即可.

  5. 四个流行的Java连接池之Proxool篇

    Proxool是一个JavaSQL Driver驱动程序,提供了对你选择的其它类型的驱动程序的连接池封装.可以非常简单的移植到现存的代码中.完全可配置.快速,成熟,健壮.可以透明地为你现存的JDBC驱 ...

  6. POJ&Tab;3740 Easy Finding

    #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using ...

  7. CentOS 7&period;2 配置mysql5&period;7

    准备篇:一.配置防火墙,开启80端口.3306端口CentOS 7.0默认使用的是firewall作为防火墙,这里改为iptables防火墙.1.关闭firewall:systemctl stop f ...

  8. java 类加载机制总结

    Java程序的运行,离不开class的加载,所谓类加载就是将编译好的class文件加载的jvm并形成class对象,只有class正确加载才能被实例化和调用. public class test { ...

  9. 软件工程作业 - word count

    (编程和软件工程作业系列) 实践最简单的项目:WC 实践是理论的基础和验证标准,希望读者贯彻“做中学”的思想,动手实现下面的项目,并和别人的成绩相比较,分析产生差距的原因. 1. 实现一个简单而完整的 ...

  10. 图解HTTP阅读笔记(1)-网络基础TCP&sol;IP

    1.TCP/IP协议族 TCP/IP这个概念对大家来说很熟悉,之前我的了解它只是一个协议.今天阅读才知道TCP/IP实际上是一个协议族,其中HTTP协议属于该协议族的一个子集.图1是TCP/IP协议族 ...