不变/协变/逆变,4.0中的这几个概念越念越象绕口令,如果单纯死记硬背,就算记住了,时间长了还是会忘记的。园子里已经有不少高手撰文写过这个话题:比如“装配脑袋”的NET 4.0中的泛型协变和反变 (2008年他就已经搞明白了这个概念)、偶像Artech的“C# 4.0新特性-"协变"与"逆变"以及背后的编程思想” 以及1-2-3的 协变(Covariance)和逆变(Contravariance)的十万个为什么
这里只是从应用的角度,简单记录一下:从.net3.5开始,System命名空间里就定义了一个泛型委托,原型如下:
public delegate TResult Func(T arg);
即:输入一个泛型参数T,返回一个泛型结果TResult假设有以下代码:
using System;
namespace in_co_contra_variant
{
class Program
{
public static void Main(string[] args)
{
Func在.net3.5环境下编译会报错:11行 fn2=fn1 这里会提示Cannot implicitly convert type 'System.Func
public delegate TResult Func(T arg);
即:在输入参数T前加了一个in,而在输出参数(也就是返回参数)前加了一个out.这样编译器就能自动将T隐式转化为T的子类,而返回类型TResult也能自动隐式转化为它的父类。说穿了就是OOP中的一个常理:子类与父类的继承关系,其实就是is a的关系,所以任何能用父类做为输入参数的地方,当然也能用子类作为替换(子承父业);而任何返回子类的地方,当然也能安全的向上转行为父类.(儿子是人类,父母当然也是人类,不可能是畜生,呵)这时,我们称T为逆变(ContraVariant)量,而TResult则为协变(CoVariant)量。记忆方法:向上转型称协变(因为这种转型肯定是安全的,比较“和谐”),向下转型称逆变(因为不一定能转型成功,有出错的可能,称逆变)最后:in,out这二个关键字不仅能用于泛型委托,同样也适用于泛型接口(比如4.0中的IEnumerable接口).
public interface IEnumerable: IEnumerable { IEnumerator GetEnumerator(); }



