IList,IEnumerable和ICollection的source code中提供的评论说
请注意
T[] : IList<T>
,我们希望确保您使用IList<YourValueType>
,我们确保可以使用YourValueType[]
没有jitting。因此TypeDependencyAttribute
上SZArrayHelper
。 这在内部是一个特殊的黑客 - 请参阅VM \ compile.cpp。
为什么IList<T>
包含对SZArrayHelper
的依赖?我理解SZArrayHelper
是一个实现IList<T>
接口的数组的CLR包装器,但我不能全面了解为什么这两个被绑在一起。
如果没有jitting,它如何确保YourValueType[]
可以使用。
答案 0 :(得分:10)
正如你的引言中所述,这是JIT中的一个黑客攻击。当VM发现TypeDependency
上有SZArrayHelper
时,它会以不同的方式处理该类,从而允许使用更高效的代码。
查看VM中的相关代码(请注意,我在这里使用较旧的公共版本 - 而不是实际的 .NET VM):
必须特别处理通过IList(或IEnumerable或ICollection)调用数组。 这些界面是“神奇的”(主要是由于工作集有关 - 它们是在内部按需创建的 即使在语义上,这些都是静态接口。)
首先,数组在.NET中是一个黑客攻击。添加通用接口时,这会产生一些问题 - 例如,int[]
是Array
,但它也是一个特殊类型,而int数组;在添加真正的泛型类型之前,这允许数组是通用的。
现在,让我们看一个具体的例子。你有一个int[]
,你想在LINQ中使用它。由于int[]
实现了IEnumerable<int>
,它为您提供了开箱即用的LINQ的全部功能,您可以编写如下内容:
var positiveNumbers = numbers.Where(i => i > 0);
从C#的角度来看,没有问题。但是,从VM的内部角度来看,这个 是一个很大的问题,因为int[]
实际上实现IEnumerable<int>
!即使在将泛型引入.NET(和C#)之后,数组仍然以旧的方式处理。
hack是使用SZArrayHelper
来处理任何这些泛型方法。因此,例如,Where
在GetEnumerator
内部调用IEnumerable<int>
。虚拟机发现您正在尝试在阵列上调用GetEnumerator
,而不是在数组实例上虚拟调度GetEnumerator
,而是将调用重定向到SZArrayHelper.GetEnumerator<int>()
。
这是一个巨大的黑客 - 如果你查看SZArrayHelper
的参考代码,你会发现大量的警告 - 例如,GetEnumerator<int>
方法是一个实例方法,但它的this
参数实际上是数组(例如int[]
),而不是SZArrayHelper
。
但它允许我们将数组视为实际实现所有这些通用接口 - 即使它们没有:)