正如您引用的内容所述,这是JIT中的一个黑客技巧。当VM发现SZArrayHelper
存在TypeDependency
时,它会特别对待该类,并允许使用更高效的代码。
查看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#)引入泛型后,数组仍然按照旧的方式处理。
解决方法是使用SZArrayHelper
来处理任何这些通用方法。例如,Where
在IEnumerable<int>
上内部调用GetEnumerator
。VM发现您正在尝试在数组上调用GetEnumerator
,而不是在数组实例上虚拟分派GetEnumerator
,它将调用重定向到SZArrayHelper.GetEnumerator<int>()
。
这是一个巨大的hack - 如果你看一下SZArrayHelper
的参考代码,你会发现有很多警告 - 例如,GetEnumerator<int>
方法是一个实例方法,但它的this
参数实际上是数组(例如int[]
),而不是SZArrayHelper
。
但它允许我们把数组视为如果它们实际上实现了所有那些通用接口——尽管它们没有 :)
YourValueType[]
本身的问题。而是在将其转换为相应的泛型接口时使用它。即使int[]
看起来实现了IEnumerable<int>.GetEnumerator
,但实际上并没有 - 其实现在那个神秘的SZArrayHelper
类中。 - Luaan