与接口的通用协方差 - "是"和"="运算符之间奇怪的行为矛盾

Hat*_*ing 5 c# generics casting interface covariance

在处理协变接口时,我有一个完整的wtf时刻.

考虑以下:

class Fruit { }
class Apple : Fruit { }

interface IBasket<out T> { }

class FruitBasket : IBasket<Fruit> { }
class AppleBasket : IBasket<Apple> { }
Run Code Online (Sandbox Code Playgroud)

注意:

  • AppleBasket 不继承自 FruitBasket.
  • IBasket协变的.

稍后在脚本中,您写道:

FruitBasket fruitBasket = new FruitBasket();
AppleBasket appleBasket = new AppleBasket();

Log(fruitBasket is IBasket<Fruit>);
Log(appleBasket is IBasket<Apple>);
Run Code Online (Sandbox Code Playgroud)

......正如您所期望的那样,输出是:

true
true
Run Code Online (Sandbox Code Playgroud)

但是,请考虑以下代码:

AppleBasket appleBasket = new AppleBasket();

Log(appleBasket is IBasket<Fruit>);    
Run Code Online (Sandbox Code Playgroud)

你期望它输出true,对吗?好吧,你错了 - 至少我的编译器是这样的:

false    
Run Code Online (Sandbox Code Playgroud)

这很奇怪,但也许它正在执行隐式转换,类似于将inta 转换为a long.An int不是一种long,但long可以隐式赋值为int.

但是,请考虑以下代码:

IBasket<Fruit> basket = new AppleBasket(); // implicit conversion?

Log(basket is IBasket<Fruit>);
Run Code Online (Sandbox Code Playgroud)

这段代码运行得很好 - 没有编译器错误或异常 - 即使我们之前已经知道AppleBasketa不是一种IBasket<Fruit>.除非有第三个选项,否则它必须在赋值中进行隐式转换.

当然basket- 宣称IBasket<Fruit>- 必须是...的一个实例IBasket<Fruit>...我的意思是,这是它被宣布为.对?

但不,根据is运营商的说法,你又错了!它输出:

false
Run Code Online (Sandbox Code Playgroud)

......意思IBasket<Fruit> fruit不是......的一个例子IBasket<Fruit>

...含义以下属性:

IBasket<Fruit> FruitBasket { get { ... } }
Run Code Online (Sandbox Code Playgroud)

有时可以返回两者都不为null的东西,并且不是它的实例IBasket<Fruit>.


此外,ReSharper告诉我,这appleBasket is IBasket<Fruit>是多余的,因为appleBasket始终是提供的类型,并且可以安全地替换为appleBasket != null...... ReSharper也错了吗?

那么,这里发生了什么?这只是我的C#版本(Unity 5.3.1p4 - 这是Unity自己的Mono分支,基于.NET 2.0)是一个坚果吗?

Dav*_*d L 1

根据您的评论,协方差没有得到正确支持是一个不小的惊喜……它是在 C#4 中添加的。

令人难以置信是,如果它的目标是基于 .NET 2.0 的端口,它甚至可以编译。