5

在 Jon Skeet 的“C# in depth”中,我正在阅读(第 511 页):

所有数组都派生自 System.Array,它们是唯一在 CLR 中直接支持的集合(我的重点)。

我想知道这到底意味着什么,尤其是对于没有这种支持的类型。所有没有这种支持的类型都是由 CLR 在解释 IL 时所做的类型组装而成的吗?CLR 是否“不知道”不具有此支持的类型?

也在第页。512本书:

C# 编译器以多种方式内置了对数组的支持。

这在某种程度上与Array 类型在 CLR 中的直接支持有关,还是这两个完全不同的东西?

4

2 回答 2

7

如前所述,数组存在于 CLR 的深处。有与它们交互的 IL 指令(ldelem.*/ stelem.*)。它们早于泛型,但允许根据需要创建不同类型的数组。这不是其他集合类型的情况 - 例如,List<T>存在于数组顶部的包装器。CLR 不需要任何特殊知识List<T>- 只需常规 IL 即可访问现有数组的内容或分配新数组。集合的另一种主要形式是链表(和类似的;树等)——但同样,它们不需要特殊支持——它们只是通过引用连接在一起的对象。

于 2013-10-31T10:35:15.403 回答
4

是的,编译器、抖动和 CLR 的三元组有很多内置的数组知识。这对于任何语言运行时都是相当重要的,它不能忽略处理器的功能。为了使代码高效运行,它必须映射到处理器可以做得很好的地方。

这不是很多,处理器是相当简单的设备。它们没有为您喜欢在程序中使用的那种数据结构提供真正的支持。语言和运行时的设计方式显示了许多底层处理器实现细节。

例如,堆栈的概念无处不在。处理器可以通过其堆栈指针直接访问的少量内存。这就是为什么 C# 语言与几乎所有语言一样,在方法中具有局部变量的概念。这是一个将存储在堆栈中的变量。处理器还强烈支持使用返回单个值的参数调用方法的概念,这在任何语言中都是通用的。为堆栈分配的少量内存为该站点命名。

值类型是底层处理器细节的另一个 .NET 示例。它们直接映射到处理器在处理值时擅长的领域。例如,一个int直接映射到处理器寄存器。Float 和 double 直接映射到处理器存储这些值并使用这两种类型执行浮点指令的能力。

数组是处理器可以支持的唯一且唯一的数据结构。很简单,一块内存,带有访问该内存的指针。 在.NET中进行了高度优化,以使其尽可能高效。特别向“向量”致敬,这是一种经过特殊处理的数组类型。起始索引为 0 的一维数组,该数组类型直接映射到处理器支持。你在 C# 中得到一个向量type[]宣言。您还可以从中看到数组限制,.NET 使创建一个起始索引不为 0 的数组变得非常困难。出于一个很好的理由,索引这样的数组是昂贵的,因为它需要额外的减法来映射内存块的索引。数组索引检查是另一个重要的优化目标,边界检查是昂贵的。抖动内置了许多智能,以识别循环永远不能索引数组越界,从而完全消除索引检查。

任何数据结构都需要建立在仅对裸阵列的处理器支持之上。这就是为什么您发现在 .NET 集合类中完成了该操作。没有来自 CLR 或抖动的额外帮助,因为他们几乎无法使其更快。这也是这些集合类都构建在向量数组之上的原因。像 List<> 一样,根本不是一个类似于您在数据算法教科书中看到的那种列表,实际上是一个数组。或者 Dictionary<>,一个数组的对立面,但仍然用它们实现。

于 2013-10-31T12:04:59.543 回答