C# 泛型委托的 逆变与协变
先说两个引子
1,类型的向上转型和向下转型
Animal类型 转为 Dog 类型为向下转型,反之。
2,泛型的转型问题
List<Dog> 不能转为 List<Animal>
为了解决上述问题,C#提供了逆变与协变的关键字,in和out。
干嘛用的?
用来声明泛型是否允许向上或向下转型。
和函数参数上的in和out关键字是否相同?
有些许联系,但完全不一样。
普通泛型上的应用在此略过,我们直接说泛型委托上的。
泛型委托的in与out
例如Func的定义的一种
public delegate TResult Func<in T, out TResult>(T arg)
在这里,请问in和out的含义是什么?
为什么要这样写? 不写行不行?
我直接说我的理解:
in代表允许T类型向下转型,out代表允许TResult类型向上转型。
可以不写,不写之后不支持转型。
例如我们列出三个继承关系的class
Object, Animal, Dog
列出两种泛型委托
delegate T FunA<out T>
delegate void FunB<in T>(T arg)
这是两种简单的in和out的情况。
我们可以分别进行尝试
FunA<Animal> a1 = () => new Animal();
FunA<Dog> a2 = a1; //报错
FunA<Object> a3 = a1;
作为一个老练的程序员,你应该能很容易看出来,第二句是有风险的。
如果该语句能执行:
Dog d = a2(); 好像没问题,但是实际上它得到的是父类Animal的实例。这将是会报错的向下转型。还好这种事编译器不会让其发生。
所以说,返回值的类型,是不可以向下转的。
因为我这个函数牌子上承诺返回Animal, 你却给我挂了个返回Dog的牌子,那怎么能行,我办不到。
所以得出结论,返回值的类型不可以向下转型(逆变,in),只可以用out修饰,或者干脆不修饰。
那么in呢?
FunB<Animal> b1 = ani => ani.someMethod();
FunB<Dog> b2 = a1;
FunB<Object> b3 = a1;//报错
我这个b1函数内,要调用Animal类型的方法
如果给我套个Object的签名,假设b3(new Object())成立。
那就是到Object实例里找Animal的方法。很明显有出错的可能。
所以得出结论,参数类型不可以向上转型(协变,out),只能用in来修饰,或者干脆修饰。
所以
网上有些文章写,因为是参数所以用in,因为是返回值所以用out。是搞错了因果关系。
因为参数不可以协变,所以不可以用out,只能用in或者不写
因为返回值不可以逆变,所以不可以用in,只能用out或者不写
至于in和out是命名上的巧合,还是微软有意为之方便大家记忆,就无从得知了
但如果想不被复杂的概念搞混,稀里糊涂的记住。搞清其内部真实原因是很有必要的。