博主之前也不是很清楚协变与逆变,今天在书上看到了有关于协变还是逆变的介绍感觉还是不太懂,后来看了一篇园子里面一位朋友的文章,顿时茅塞顿开。本文里面会有自己的一些见解也会引用博友的一些正文,希望通过本篇,能让大家对协变与逆变不再陌生。
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函数中添加:
- Action<Animal> actionAnimal = new Action<Animal>(a => {/*让动物叫*/ });
- Action<Dog> actionDog = actionAnimal;
- 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