1.C#3.0以前的协变与逆变

  如果你是第一次听说这个两个词,别担心,他们其实很常见。C#4.0中的协变与逆变[1](Covariance and contravariance)有了进一步的完善,主要是两种运行时的(隐式)泛型类型参数转换。简单来讲,所谓协变(Covariance)是指把类型从“小”升到“大”,比如从子类升级到父类;逆变则是指从“大”变到“小”,两者各有不同的条件和用途。下面的例子演示了C#3.0以前对协变与逆变支持[2] :

代码1 
       public class Animal { }
       public class Cat : Animal { }

public delegate Animal AniHandler(Animal a);
       public static Animal AniMethod(Animal a) { return null; }
       public static Cat CatMethod(Object o) { return null; }

public static void TestCovariance()
       {
           AniHandler handler1 = AniMethod;
           AniHandler handler2 = CatMethod;//这里是合法的
       }
  这里的CatMethod虽然不是严格满足委托AniHandler的签名,但它被用作AniHandler是合法的,在协变(Cat->Animal)和逆变(object->Animal)的作用下,委托指向的方法中,传入的参数可以是一个大的,宽泛的类型,而返回出来的结果可以是一个更小的,精确的类型(子类),因为它包含了更多的信息。注意这里是站在方法里面这样说的,而在调用者使用方法的角度,恰恰是相反的,在调用方法时,参数可以是一个“小”的子类,而返回值可以用作一个“大”的父类,如下面的调用是合法的:

object o = AniMethod(new Cat());
  呵呵,听上去有点晕,现在我要试着把问题简洁地表达清楚。无论是协变还是逆变,它都是为了让这样一个非常合理的事实成立:如果提供的类型信息比所需要的类型信息多(而不是相等),那这当然是可以的。在代码1的例子中,AniHandler委托需要一个Animal作为返回值,但是我返给它一个Cat,Cat包含了Animal的所有特征,这当然是可以的,这就是协变;同时AniHandler需要一个Animal作为参数,为了让函数获得的信息比要求的多,我可以只要求传进来一个object,这也当然是可以的,这就是逆变。

2.C#4.0中的协变

  我们先来看一下和谐的协变是如何发生的。C#4.0中的协变与C#3.0中的宽松委托非常类似,新的C#协变特征还体现在泛型接口或者泛型委托的类型参数上。还是以经典的Animal和Cat为例,在你看过上面代码1之后,既然Cat CatMethod()可以被用作Animal AniHandler,那么你完全有理由相信下面的代码在C#3.0中也是合法的:

代码3 
        delegate T THandler<T>();

static void Main(string[] args)
        {            
            THandler<Cat> catHandler= () => new Cat();
            THandler<Animal> aniHandler = catHandler;//Covariance 
        }

  很遗憾,您错了,在C#3.0中,上面的代码不能通过编译,你会被告知这样的错误:

  时代进步了,现在在C#4.0的编译器是支持上面的写法的。你只需要在声明THandler的类型参数前加一个out关键字即可:

delegate T THandler<out T>();
 
  单独的使用一个关键字而不是直接允许隐式转换也是为了类型安全的考虑。所以当你写下out的时候,就应该知道可能发生的Covariance。

3.C#4中的逆变

  我们继续使用Animal和Cat的例子,在VS2008中,以下的代码不能通过编译:

代码5 
       delegate void THandler<T>(T t);    
       public static void TestContravariance()
       {
           THandler<Animal> aniHandler = (ani) => { };
           THandler<Cat> catHandler = aniHandler;
       }
  而在VS2010中,呃,同样不能。呵呵,其实就差一点点,这里如果在类型参数T前面加上关键字“in”,即delegate void THandler<in T>(T t);就可以实现Cat->Animal的Contravariance。

4.总结

  C#4中的协变和逆变使得泛型编程时的类型转换更加自然,不过要注意的是上面所说的协变和逆变都只作用于引用类型之间,而且目前只能对泛型接口和委托使用。一个T参数只能是in或者是out,你如果即想你的委托参数逆变又想返回值协变(如代码1所示),是做不到的。

c#4.0新特性之协变与逆变的更多相关文章

  1. C# 新特性_协变与逆变 (.net 4.0)

    C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...

  2. C#2.0新增功能06 协变和逆变

    连载目录    [已更新最新开发文章,点击查看详细] 在 C# 中,协变和逆变能够实现数组类型.委托类型和泛型类型参数的隐式引用转换. 协变保留分配兼容性,逆变则与之相反. 以下代码演示分配兼容性.协 ...

  3. .NET 4.0中的泛型协变和逆变

    随Visual Studio 2010 CTP亮相的C#4和VB10,虽然在支持语言新特性方面走了相当不一样的两条路:C#着重增加后期绑定和与动态语言相容的若干特性,VB10着重简化语言和提高抽象能力 ...

  4. C#4.0泛型的协变,逆变深入剖析

    C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...

  5. .NET 4.0中的泛型的协变和逆变

    转自:http://www.cnblogs.com/jingzhongliumei/archive/2012/07/02/2573149.html 先做点准备工作,定义两个类:Animal类和其子类D ...

  6. 转载.NET 4.0中的泛型的协变和逆变

    先做点准备工作,定义两个类:Animal类和其子类Dog类,一个泛型接口IMyInterface<T>, 他们的定义如下:   public class Animal { } public ...

  7. Java协变、逆变、类型擦除

    协变.逆变 定义 Java中String类型是继承自Object的,姑且记做String ≦ Object,表示String是Object的子类型,String的对象可以赋给Object的对象.而Ob ...

  8. C#4.0新特性(3):变性 Variance(逆变与协变)

    一句话总结:协变让一个粗粒度接口(或委托)可以接收一个更加具体的接口(或委托)作为参数(或返回值):逆变让一个接口(或委托)的参数类型(或返回值)类型更加具体化,也就是参数类型更强,更明确. 通常,协 ...

  9. c# 4.0新特性一览

    原文:http://www.cnblogs.com/palo/archive/2009/03/01/1400949.html 终于静下心来仔细听了一遍Anders Hejlsberg(Visual S ...

随机推荐

  1. pb popmenu弹出式选单位置的问题

    在主界面上使用m_main.m_title.PopMenu(PointX(),PointY()),可以正确定位弹出式选单的位置: 在主界面的控件智商为使用m_main.m_title.PopMenu( ...

  2. Runtime 在IOS中的详细使用

    因为之前写东西的时候,都在未知笔记中,所有大家没有看到过我的文章,今天就开始使用博客园来进行和大家沟通.好了,废话不那么多了,转入正题.下面我把runtime 给大家介绍一下. ### 一.runti ...

  3. 10.21_Nutz批量插入顺序,POI,wiki持续关注,POI,SSH,数据库优先

    (1)Nutz,dao的批量插入会关注顺序吗? http://www.douban.com/group/topic/64322582/ (2)工作需要优先!!!  POI,SSH,数据库管理及plsq ...

  4. HDU 4628 Pieces(DP + 状态压缩)

    Pieces 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4628 题目大意:给定一个字符串s,如果子序列中有回文,可以一步删除掉它,求把整个序列删除 ...

  5. Windows下ANSI、Unicode、UTF8字符编码转换

    主意:输入字符串必须是以'\0'结尾,如果输入字符串没有以'\0'结尾,请手动设置,否则转换会有错误. unsigned int EncodeUtil::AnsiToUcs2( char* pAnsi ...

  6. .net闭包的应用

    这里体现出闭包的数据共享 , , , , , , , , , }; ; ; values.ToList().ForEach(s => result1 += s); values.ToList() ...

  7. JS如果阻止事件冒泡和浏览器默认事件

    原地址:http://missra.com/article/web-57.html 嵌套的标签元素,如果父元素和子元素都绑定了一些事件,那么在点击最内层子元素时可能会触发父级元素的事件,下面介绍一下J ...

  8. 解决IE6下不支持 png24的透明图片问题

    常用的两种解决方案: 第一:使用IE滤镜解决 关键代码: css代码  _background:none;_filter:progid:DXImageTransform.Microsoft.Alpha ...

  9. jquery插件----文件上传uploadfile

    使用了一款jquery上传的插件,ajax上传,可以显示上传的进度,高可配性,简要记录. 插件地址http://hayageek.com/docs/jquery-upload-file.php git ...

  10. jbpm4.4 spring整合

    jBPM-4.4与Spring集成配置比较容易,这里我使用的是Spring-2.5.6,数据库连接池使用C3P0,将相关的两个jar文件加入到CLASSPATH中. jBPM-4.4与Spring集成 ...