在处理协变接口(interface)时,我有一个完整的 wtf 时刻。
考虑以下几点:
class Fruit { }
class Apple : Fruit { }
interface IBasket<out T> { }
class FruitBasket : IBasket<Fruit> { }
class AppleBasket : IBasket<Apple> { }
注意:
-
AppleBasket
不继承自FruitBasket
. -
IBasket
是协变的。
稍后在脚本中,您编写:
FruitBasket fruitBasket = new FruitBasket();
AppleBasket appleBasket = new AppleBasket();
Log(fruitBasket is IBasket<Fruit>);
Log(appleBasket is IBasket<Apple>);
...如您所料,输出是:
true
true
但是,请考虑以下代码:
AppleBasket appleBasket = new AppleBasket();
Log(appleBasket is IBasket<Fruit>);
你期望它输出 true
, 正确的?好吧,你错了——至少对我的编译器来说是这样:
false
这很奇怪,但也许它正在执行一个隐式转换,类似于转换 int
到 long
.一个int
不是一种 long
, 但 long 可以隐式地分配 int 的值。
但是,请考虑以下代码:
IBasket<Fruit> basket = new AppleBasket(); // implicit conversion?
Log(basket is IBasket<Fruit>);
此代码运行良好 - 没有编译器错误或异常 - 即使我们之前了解到 AppleBasket
不是一种 IBasket<Fruit>
.除非有第三种选择,否则它必须在赋值中进行隐式转换。
当然basket
- 声明为 IBasket<Fruit>
- 必须 是 IBasket<Fruit>
的实例... 我的意思是,这就是它被宣布的内容。对吧?
但是不,根据is
运算符,你又错了!它输出:
false
...意思IBasket<Fruit> fruit
不是 IBasket<Fruit>
的实例……嗯?
...表示以下属性:
IBasket<Fruit> FruitBasket { get { ... } }
有时可以返回一些两者都不为空,并且不是IBasket<Fruit>
的实例的东西.
进一步,ReSharper 告诉我appleBasket is IBasket<Fruit>
是多余的 appleBasket
始终 是提供的类型,可以安全地替换为 appleBasket != null
... ReSharper 也弄错了吗?
那么,这里发生了什么?这只是我的 C# 版本(Unity 5.3.1p4 - 它是 Unity 自己的 Mono 分支,基于 .NET 2.0)是疯子吗?
最佳答案
根据您的评论,协方差未得到适当支持并不令人意外……它是在 C#4 中添加的。
是 令人难以置信的是,如果它针对基于 .NET 2.0 的端口,它甚至可以编译
关于c# - 接口(interface)的通用协方差 - "is"和 "="运算符之间奇怪的行为矛盾,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36731969/