IEnumerable<T>是 协变的, 但它不支持值类型,仅支持引用类型。下面的简单代码已成功编译:
IEnumerable<T>
IEnumerable<string> strList = new List<string>(); IEnumerable<object> objList = strList;
但是从更改string为int会得到编译错误:
string
int
IEnumerable<int> intList = new List<int>(); IEnumerable<object> objList = intList;
原因在MSDN中进行了解释:
方差仅适用于引用类型;如果为变量类型参数指定值类型,则该类型参数对于生成的构造类型而言是不变的。
我搜索后发现,提到的一些问题的原因是 值类型和引用类型之间的装箱 。但这仍不能使我清楚为何拳击是原因?
有人可以给出一个简单而详细的解释,为什么协变量和协变量不支持值类型,以及 装箱 如何影响值类型?
基本上,当CLR可以确保不需要对值进行任何 表示性更改 时,就会应用差异。引用看起来都一样- 因此您可以IEnumerable<string>在IEnumerable<object>不更改表示形式的情况下将其用作。本机代码本身根本不需要知道您正在使用这些值做什么,只要基础结构保证它肯定是有效的即可。
IEnumerable<string>
IEnumerable<object>
对于值类型,这是行不通的-将a IEnumerable<int>视为IEnumerable<object>,使用该序列的代码将必须知道是否执行装箱转换。
IEnumerable<int>
通常,您可能想阅读Eric Lippert 关于表示和身份的博客文章,以获取更多有关该主题的信息。
编辑:我自己重新阅读了Eric的博客文章,尽管两者是相互联系的,但至少与 身份 同样重要。特别是:
这就是为什么接口和委托类型的协变和协变转换要求所有不同类型的参数都是引用类型的原因。为了确保变体引用转换始终保留身份,所有涉及类型实参的转换也必须保留身份。确保类型参数上的所有非平凡转换都保留身份的最简单方法是将它们限制为引用转换。