何时使用包含引用类型的值类型的数组而不是引用类型的数组?

假设我有以下内容:

public class MyElement { } [Serializable] [StructLayout(LayoutKind.Sequential)] struct ArrayElement { internal MyElement Element; } public class MyClass { internal MyElement ComputeElement(int index) { // This method does a lengthy computation. // The actual return value is not so simple. return new MyElement(); } internal MyElement GetChild(ref MyElement element, int index) { if (element != null) { return element; } var elem = ComputeElement(index); if (Interlocked.CompareExchange(ref element, elem, null) != null) { elem = element; } return elem; } } public class MyClassA : MyClass { readonly MyElement[] children = new MyElement[10]; public MyElement GetChild(int index) { return GetChild(ref children[index], index); } } public class MyClassB : MyClass { readonly ArrayElement[] children = new ArrayElement[10]; public MyElement GetChild(int index) { return GetChild(ref children[index].Element, index); } } 

在什么情况下使用MyClassB优于MyClassA

澄清usr的正确但有点稀疏的答案:

C#支持一个function – 我的“C#中最糟糕的function”的候选者 – 称为数组类型协方差 。 也就是说,如果你有一个海龟数组,你可以将它分配给一个类型为“动物数组”的变量:

 class Animal {} class Turtle : Animal {} ... Animal[] animals = new Turtle[10]; 

这是“协方差”,因为数组的赋值兼容性规则是与其元素的赋值兼容性规则方向相同的箭头

 Turtle --> Animal Turtle[] --> Animal[] 

这个function不是类型安全的,因为,…

 animals[0] = new Giraffe(); 

我们只是将长颈鹿放入一个实际上是一系列海龟的arrays中。 编译器无法确定此处是否违反了类型安全性 – 长颈鹿是动物 – 因此检查必须由运行时执行。

为了防止在运行时发生这种情况,每次将Giraffe放入动物数组时,运行时都会插入一个检查,以检查它是否真的是一个海龟数组。 这几乎从来都不是。 但是这种检查需要时间,因此该function有效地减慢了每次成功访问arrays的速度

不安全的数组协方差仅适用于其元素类型为引用类型的数组。 它不适用于值类型。 (这是一个小谎言; CLR将允许你将int[]object ,然后对uint[] 。但一般来说,协方差不适用于值类型。)

因此,您可以通过使数组实际上是值类型数组来节省检查费用,其中值类型只是引用的包装器。 数组的大小不受影响,但访问它的速度会稍快一些。

你不应该采取这些疯狂的技巧,除非你有经validation据表明这样做实际上解决了实际的性能问题 。 保证这种优化的情况非常少,但有一些地方可以使这种事情发挥作用。

我注意到你也可以通过密封 Turtle类型然后使用一系列海龟来避免检查的成本。 运行时将推断数组类型实际上不能更多地派生,因为它的元素类型将从密封类型派生,这是不可能的。

ArrayElement是一个包装器,允许JIT生成更好的代码。 .NET数组具有对引用存储的运行时类型检查,因为它们在所有方面都不是静态类型安全的。

 var array = new Stream[10]; ((object[])array)[0] = "somestring"; //runtime exception 

使用包装器不再需要类型检查。