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

cub*_*729 6 c# arrays caching value-type

假设我有以下内容:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在什么情况下使用MyClassB结束会有什么好处MyClassA

Eri*_*ert 12

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

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

class Animal {}
class Turtle : Animal {}
...
Animal[] animals = new Turtle[10];
Run Code Online (Sandbox Code Playgroud)

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

Turtle --> Animal
Turtle[] --> Animal[]
Run Code Online (Sandbox Code Playgroud)

这个功能不是类型安全的,因为,...

animals[0] = new Giraffe();
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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