Когда использовать массив типа значение содержит ссылку на массив ссылочного типа?

Предположим, у меня есть следующие:

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?

2 ответов


чтобы уточнить правильный, но несколько разреженный ответ usr:

C# поддерживает функцию -- мой кандидат на "худшую функцию в C#" -- called тип массива ковариации. То есть, если у вас есть массив черепах, вы можете присвоить его переменной типа "массив животных":

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

это "ковариация", потому что правило совместимости присваивания для массивов является стрелкой в том же направлении как правило совместимости назначения для его элементы:

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

это функция не введите safe, потому что хорошо...

animals[0] = new Giraffe();

и мы просто поместили жирафа в массив, который на самом деле является массивом черепах. Компилятор не может определить, что безопасность типов нарушена здесь - жираф-это животное , поэтому проверка должна выполняться средой выполнения.

чтобы предотвратить это во время выполнения, среда выполнения вставляет проверку каждый раз, когда вы помещаете жирафа в массив животных, чтобы проверьте, если это действительно массив черепах. Чего почти никогда не бывает. Но эта проверка занимает время, и поэтому функция эффективно тормозит каждый успешный доступ к массиву.

ковариация небезопасного массива применяется только к массивам, типы элементов которых ссылка типа. Он не применяется к типам значений. (Это небольшая ложь; CLR позволит вам бросить int[] to object а то object до uint[]. Но в целом ковариация не применяется к типам значений.)

поэтому вы можете сэкономить за счет проверки, сделав свой массив фактически массивом типа значения, где тип значения является просто оболочкой для ссылки. Размер массива останется неизменным, но доступ к нему будет несколько быстрее.

вы не должны тянуть эти виды сумасшедших трюков, если у вас нет эмпирических доказательств что это на самом деле решает практическую проблему производительности. Количество ситуаций, в которых эта оптимизация оправдана, довольно мало, но есть несколько мест, где такие вещи могут иметь значение.

Я отмечаю, что вы также можете избежать стоимость проверки уплотнения тип черепахи, а затем с помощью массива черепах. Среда выполнения будет причиной того, что тип массива не может быть!--10-->действительно быть более производным, потому что тогда его тип элемента будет производным от запечатанного типа, что невозможно.


ArrayElement - Это оболочка, которая позволяет JIT генерировать лучший код. Массивы .NET имеют проверки типа среды выполнения для ссылочных хранилищ, поскольку они не являются статически безопасными во всех отношениях.

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

с оберткой проверка типа больше не требуется.