zoukankan      html  css  js  c++  java
  • 那些年搞不懂的"协变"和"逆变"

      博主之前也不是很清楚协变与逆变,今天在书上看到了有关于协变还是逆变的介绍感觉还是不太懂,后来看了一篇园子里面一位朋友的文章,顿时茅塞顿开。本文里面会有自己的一些见解也会引用博友的一些正文,希望通过本篇,能让大家对协变与逆变不再陌生。

    What's 协变逆变?

      从字面理解协变就是"妥协的变化",而逆变则是"逆天的变化",哈哈,并不标准,我们来看看MSDN的解释:  

        “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。

        “逆变”则是指能够使用派生程度更小的类型。

      解释的很正确,大致就是这样,不过不够直白。通俗来讲,协变就是从小到大,逆变则是从大到小。。协变逆变只会出现在泛型类和委托中,下面我们通过例子来了解协变和逆变。

      

      按照我们的理解,Dog继承自Animal,所以Animal aAnimal = aDog; aDog 编辑器会隐式的转变为Animal。但是List<Dog> 不继承List<Animal> ,它们的身份是相等的,所以转换失败。

      如果想要转换的话我们可以使用以下的代码:

      List<Animal> animals= dogs.Select(c=>(Animal)c).ToList();

      这种写法太麻烦了,有没有一种方式可以直接赋值呢?答案是可以的,微软为了帮助我们开发人员简化这种写法,帮我们定义了Out和in两个关键字,我们看下官方对这两个关键字的解释:

      

      

      Out关键字可以简单的定义为返回值参数,只能作为返回值。

      In关键字则定义为输入型参数,其含义指In对应的类型在泛型成员中不能作为返回值。

      和刚开始说的一样,T 用out 标记,所以T代表了输出,也就是只能作为结果返回。

      

      因为T只能做结果返回,所以T不会被修改,编译器就可以推断下面的语句强制转换合法,所以一下代码编译通过

        IEnumerable<Animal> animals = dogs;  

     

      再看逆变,在Main函数中添加:

    1. Action<AnimalactionAnimal = new Action<Animal>(a => {/*让动物叫*/ });  
    2. Action<DogactionDog = actionAnimal;  
    3. actionDog(aDog); 

      很明显actionAnimal 是让动物叫,因为Dog是Animal,那么既然Animal 都能叫,Dog肯定也能叫。

      In 关键字:逆变,代表输入,代表着只能被使用,不能作为返回值,所以C#编译器可以根据in关键字推断这个泛型类型只能被使用,所以Action<Dog> actionDog = actionAnimal;可以通过编译器的检查。

      再次演示Out关键字:添加两个类:

      

      out关键字在上图中只用于返回值,所以编辑器不会报错。我们再来修改一下类

      

      如果泛型参数标记为out,泛型类成员参数又定义了T类型的话则编译不通过。同样的方法我们来测试下逆变:

      

    总结

      Out:代表协变,只能当返回值类型使用,不能作为方法实参

      In:只能用作方法实参,不能用作返回值类型。

      我觉得只要上面两个概念搞懂了,在自己敲下Demo就没有问题了。

    本文参考:http://www.cnblogs.com/majiang/articles/2607990.html

      

  • 相关阅读:
    PHP之目录遍历
    PHP之验证码
    PHP之验证码
    PHP之异常处理模式
    PHP之pdo的预处理模式
    PHP之PDO
    PHP之cookie和session
    PHP之MVC
    单例模式
    ThreadLocal
  • 原文地址:https://www.cnblogs.com/dazhuangtage/p/5894687.html
Copyright © 2011-2022 走看看