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

What's 协变逆变?

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

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

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

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

  那些年搞不懂的"协变"和"逆变"-LMLPHP

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

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

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

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

  那些年搞不懂的&quot;协变&quot;和&quot;逆变&quot;-LMLPHP

  那些年搞不懂的&quot;协变&quot;和&quot;逆变&quot;-LMLPHP

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

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

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

  那些年搞不懂的&quot;协变&quot;和&quot;逆变&quot;-LMLPHP

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

    IEnumerable<Animal> animals = dogs;

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

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

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

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

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

  那些年搞不懂的&quot;协变&quot;和&quot;逆变&quot;-LMLPHP

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

  那些年搞不懂的&quot;协变&quot;和&quot;逆变&quot;-LMLPHP

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

  那些年搞不懂的&quot;协变&quot;和&quot;逆变&quot;-LMLPHP

总结

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

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

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

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

  

05-11 16:56