Когда использовать массив типа значение содержит ссылку на массив ссылочного типа?
Предположим, у меня есть следующие:
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
с оберткой проверка типа больше не требуется.