繁体   English   中英

使用引用类型和值类型参数化的泛型类型的隐式转换

[英]Implicit convertion of generic types parameterized with reference types vs value types

为什么C#隐式地将使用实现接口的引用类型参数化的泛型类型转换为使用实现的接口参数化的相同泛型类型,但不对引用类型执行相同的隐式转换?

从本质上讲,为什么第一行编译但第二行失败?

IEnumerable<IComparable<Version>> x = Enumerable.Empty<Version>();
IEnumerable<IComparable<int>> y = Enumerable.Empty<int>();

特别棒的是对描述此行为的规范部分的引用。

简短的回答

尽管名称是“隐式”,但隐式转换不适用,除非规则明确说明它们,并且规则在从IEnumerable<int>转到IEnumerable<IComparable<int>>时不允许装箱转换。 作为一个更简单的例子,出于同样的原因,你不能从IEnumerable<int>转到IEnumerable<object> ,并且这个案例有很好的记录。

答案很长

OK,首先,为什么 IEnumerable<T>转换到IEnumerable<IComparable<T>>呢? 这将在§6.1.6( C#语言规范5.0 )中介绍:

隐式引用转换是:

[...]

  • 从任何引用类型到接口或委托类型T如果它具有隐式标识或引用转换到接口或委托类型T 0T 0是方差可转换(第13.1.3.2节)到T

并且§13.1.3.2说:

A型T<A 1 , …, A n >是方差-转换为一个类型T<B 1 , …, B n >如果T是一个接口或与变体类型参数中声明委托类型T<X 1 , …, X n > ,并且对于每个变体类型参数X i ,以下之一成立:

  • X i是协变的,并且从A iB i存在隐式参考或身份转换

由于IEnumerable<T>T是协变的,这意味着如果存在从TIComparable<T>的隐式引用或标识转换,则存在从IEnumerable<T>IEnumerable<IComparable<T>>的隐式引用转换。 ,由于这些是方差可兑换的。

当然,我强调“参考”是有原因的。 由于Version实现了IComparable<Version> ,因此存在隐式引用转换:

  • 从任何类型 S到任何接口类型 T ,只要S实现T

是的,现在,为什么IEnumerable<int>隐式转换为IEnumerable<IComparable<int>> 毕竟, int隐式转换为IComparable<int>

IComparable<int> x = 0;  // sure

但它不是通过引用转换或身份转换,而是通过装箱转换(第6.1.7节):

从任何非可空值类型 [...]到由非可空值 类型实现的任何接口类型存在装箱转换。

§13.1.3.2的规则在考虑是否可以进行方差转换时不允许装箱转换,并且没有其他规则可以实现从IEnumerable<int>IEnumerable<IComparable<int>>的隐式转换。 尽管名称如此,隐式转换仍由明确的规则涵盖。

实际上有一个更简单的说明这个问题:

object x = 0;  // sure, an int is an object
IEnumerable<object> x = new int[] { 0 };  // except when it's not

出于同样的原因,这是不允许的:没有从intobject引用转换,只有一个装箱转换,并且不考虑这些转换。 在这种形式中,Stack Overflow上有几个问题可以解释为什么不允许这样 (比如这个 )。 总结一下:这不是不可能的 ,但是为了支持它,编译器必须生成支持代码以便在某处粘贴用于装箱转换的代码。 C#团队重视这种情况下的易用性,并决定仅允许保留身份的转换。

最后,作为一个实际考虑的问题,假设您有一个IEnumerable<int>并且您需要一个IEnumerable<IComparable<int>> ,您将如何获得它? 好吧,自己做拳击:

Func<int, IComparable<int>> asComparable = i => i;  // compiles to ldarg ; box ; ret
IEnumerable<IComparable<int>> x = Enumerable.Empty<int>().Select(asComparable);

当然使用Enumerable.Cast在这里会更实用; 我这样写是为了强调隐含的转换。 这项行动需要付出代价,而这正是重点所在。 C#设计师希望这个成本是明确的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM