C#4.0新特性之(三)协变与逆变

C#4.0新特性之(三)协变与逆变

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所示),是做不到的。

5.引用

[1] http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

[2] http://msdn.microsoft.com/zh-cn/library/ms173174(VS.80).aspx

 

Author: Freesc Huang @ CNBlogs

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值