"IList<T>,IEnumerable<T>和ICollection<T>的TypeDependencyAttribute("System.SZArrayHelper")的目的是什么?"

8
在IList、IEnumerable和ICollection的源代码中提供的注释说:
注意,T[]: IList<T>,如果您使用IList<YourValueType>,我们希望确保YourValueType[]可以在没有 jitting 的情况下使用。因此,在SZArrayHelper上应用了TypeDependencyAttribute。这是一个特殊的内部黑客技巧,请参阅VM\compile.cpp。
为什么IList<T>包含对SZArrayHelper的依赖?我理解SZArrayHelper是实现IList<T>接口的数组的CLR包装器,但我不明白它们为什么要绑定在一起。
那么,它如何确保可以在没有jitting的情况下使用YourValueType[]?

可能是因为99.9%的集合在某个层面上使用数组进行存储,所以要求这种依赖关系是合理的。这是否给您带来了问题,还是您只是好奇呢? - D Stanley
@DStandley:好奇心..但是它如何确保YourValueType[]在不进行JIT编译的情况下可以使用? - NullReference
1
这并不是关于 YourValueType[] 本身的问题。而是在将其转换为相应的泛型接口时使用它。即使 int[] 看起来实现了 IEnumerable<int>.GetEnumerator,但实际上并没有 - 其实现在那个神秘的 SZArrayHelper 类中。 - Luaan
1个回答

12

正如您引用的内容所述,这是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来处理任何这些通用方法。例如,WhereIEnumerable<int>上内部调用GetEnumerator。VM发现您正在尝试在数组上调用GetEnumerator,而不是在数组实例上虚拟分派GetEnumerator,它将调用重定向到SZArrayHelper.GetEnumerator<int>()

这是一个巨大的hack - 如果你看一下SZArrayHelper的参考代码,你会发现有很多警告 - 例如,GetEnumerator<int>方法是一个实例方法,但它的this参数实际上是数组(例如int[]),而不是SZArrayHelper

但它允许我们把数组视为如果它们实际上实现了所有那些通用接口——尽管它们没有 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接