专栏首页菩提树下的杨过c#4.0中的不变(invariant)、协变(covariant)、逆变(contravariant)小记

c#4.0中的不变(invariant)、协变(covariant)、逆变(contravariant)小记

不变/协变/逆变,4.0中的这几个概念越念越象绕口令,如果单纯死记硬背,就算记住了,时间长了还是会忘记的。

园子里已经有不少高手撰文写过这个话题:比如“装配脑袋”的NET 4.0中的泛型协变和反变 (2008年他就已经搞明白了这个概念)、偶像Artech的“C# 4.0新特性-"协变"与"逆变"以及背后的编程思” 以及1-2-3的 协变(Covariance)和逆变(Contravariance)的十万个为什么

这里只是从应用的角度,简单记录一下:

从.net3.5开始,System命名空间里就定义了一个泛型委托,原型如下:

public delegate TResult Func<T, TResult>(T arg);

即:输入一个泛型参数T,返回一个泛型结果TResult

假设有以下代码:

using System;
namespace in_co_contra_variant
{
    class Program
    {
        public static void Main(string[] args)
        {
            Func<object, ArgumentException> fn1 = Test;
            ArgumentException e1 = fn1("aaaaa");
            Func<string, Exception> fn2 = null;
            fn2 = fn1;
            Exception e2 = fn2("bbbbb");
            Console.WriteLine("e1:{0}\te2:{1}", e1.Message, e2.Message);
            Console.Read();
        }

        public static ArgumentException Test(object obj) 
        {
            return new ArgumentException(obj.ToString());
        }        
    }
}

在.net3.5环境下编译会报错:11行 fn2=fn1 这里会提示

Cannot implicitly convert type 'System.Func<object,System.ArgumentException>' to 'System.Func<string,System.Exception>' 

即:无法隐式将System.Func<object,System.ArgumentException>转换成System.Func<string,System.Exception>

说得更白一点,4.0以前的泛型委托,泛型参数一旦在实例使用过程中明确为具体类型后,是不能隐式自动转换成其它类型的,哪怕类型是兼容的(按道理来讲,fn1中的输入参数类型为object,由于string是继承自object的,所以能用object的地方,string应该是能用的;同理:fn1中(返回)输出参数类型ArumentException继承自Exception,所以返回类型ArgumentException可以向上的转化为Exception不会有任何问题,所以说fn1中的参数类型与fn2中的参数类型是安全兼容的,但是编译回不允许),这种不允许泛型参数类型变化的特点,称为不变性(invariant).

而在4.0中,上面的代码可正常编译运行,如果研究下4.0中Func中的原型,会发现多了二个关键字:

public delegate TResult Func<in T, out TResult>(T arg);

即:在输入参数T前加了一个in,而在输出参数(也就是返回参数)前加了一个out.

这样编译器就能自动将T隐式转化为T的子类,而返回类型TResult也能自动隐式转化为它的父类。

说穿了就是OOP中的一个常理:子类与父类的继承关系,其实就是is a的关系,所以任何能用父类做为输入参数的地方,当然也能用子类作为替换(子承父业);而任何返回子类的地方,当然也能安全的向上转行为父类.(儿子是人类,父母当然也是人类,不可能是畜生,呵)

这时,我们称T为逆变(ContraVariant)量,而TResult则为协变(CoVariant)量。记忆方法:向上转型称协变(因为这种转型肯定是安全的,比较“和谐”),向下转型称逆变(因为不一定能转型成功,有出错的可能,称逆变)

最后:in,out这二个关键字不仅能用于泛型委托,同样也适用于泛型接口(比如4.0中的IEnumerable接口)

    public interface IEnumerable<out T> : IEnumerable
    {
        
        IEnumerator<T> GetEnumerator();
    }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Flash/Flex学习笔记(36):自己动手实现一个滑块控件(JimmySilder)

    先看最终的演示: 滑块条的应用实在太广泛了:mp3播放器中声量的大小控制,视频播放时的画面亮度调节,阅读新闻时字体大小的实时调整,对象的大小互动控制... 分析...

    菩提树下的杨过
  • [biztalk笔记]-1.Hello World!

    开始接触biztalk了,这个东西感觉不象linq,silverlight等具体的技术好学,看了几天文档,也跑通了一些小示例,但是仍然觉得毫无感觉,只大概的知道...

    菩提树下的杨过
  • ActiveMQ笔记(4):搭建Broker集群(cluster)

    上一篇介绍了基于Networks of Borkers的2节点HA方案,这一篇继续来折腾Networks of Brokers,当应用规模日渐增长时,2节点的b...

    菩提树下的杨过
  • 机器学习(四)——梯度下降算法解释以及求解

    机器学习(四) ——梯度下降算法解释以及求解θ (原创内容,转载请注明来源,谢谢) (本文接机器学习(二)的内容) 一、解释梯度算法 ? 梯度算法公式以及简化的...

    企鹅号小编
  • 机器学习(四) ——梯度下降算法解释以及求解θ

    机器学习(四)——梯度下降算法解释以及求解θ (原创内容,转载请注明来源,谢谢) (本文接 机器学习(二) 的内容) 一、解释梯度算法 ? ? 梯度算法公式...

    用户1327360
  • 【编程基础第七讲】如何编写有界面的程序?

    存在问题: 好多小伙伴都有一个疑问,我我们学编程貌似都是看输出,怎么才能搞个像window上程序带个又界面的,眼见为实嘛 解决方案: 很多群友都在问学习了C语言...

    程序员互动联盟
  • AI 时代的人机协同创作

    这是mixlab社区成员00-ML04的文章,总结了她在4月的一次活动中分享的内容。

    mixlab
  • -企业面试题: display:none和visibility:hidden的区别?

    - display:none 隐藏对应的元素,在文档布局中不再给它分配空间,它各边的元素会合拢,就当他从来不存在。

    舒克
  • 如何设置CentOS 7开机自动获取IP地址详解

    本例中以CentOS 7举例说明如何设置Linux开机自动获取IP地址和设置固定IP地址。

    yaohong
  • 用SVG实现一个优雅的提示框

    Tooltips常被称为提示框(或信息提示框),提示框能够以较强的交互性、自由度为用户提供相应的提示信息。今天我们要聊的不是如何实现强大的交互行为,而是来看看如...

    ConardLi

扫码关注云+社区

领取腾讯云代金券