c# - 接口(interface)的通用协方差 - "is"和 "="运算符之间奇怪的行为矛盾

标签 c# generics interface casting covariance

在处理协变接口(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    

这很奇怪,但也许它正在执行一个隐式转换,类似于转换 intlong .一个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/

相关文章:

c# - Linq 查询不返回值

c# - 表达式树和 AND 条件

Swift 4——尝试使用泛型来减少公式化代码的挑战

java - 标准 - 通过 getter 选择

java - 将接口(interface)转换为RMI接口(interface)

c# - 使用 XmlSerializer 创建具有属性和值但没有子元素的元素

c# - 如何在给定 Dictionary<TKey,TValue> 类型的情况下获取 TKey 和 TValue 的类型

java - 使用 Spring Rest 将 ID 转换为类类型

javascript - 使用具有可选属性的对象循环遍历数组,但始终完全循环

c# - 释放 COM 对象的正确方法?