.NET

C# 泛型委托的 逆变与协变

2021-05-10  本文已影响0人  Gascognya

先说两个引子

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是命名上的巧合,还是微软有意为之方便大家记忆,就无从得知了


但如果想不被复杂的概念搞混,稀里糊涂的记住。搞清其内部真实原因是很有必要的。


半夜床上手机码的字,排版不好还请见谅

上一篇下一篇

猜你喜欢

热点阅读