В определенном контексте мне нужно управлять ограниченными значениями. Для упрощения; давайте просто скажем, что мне нужно ограничить значения либо строкой, либо 64-битным целым числом.Использование «union struct», чтобы избежать приведения в действие/cast/boxbox
Для этой цели; Я рассматриваю объявление типа структуры, которое имеет одно поле для типа сохраняемого значения и одно поле для фактического значения.
В этом упрощенном случае поле типа может быть опущено, поскольку мы можем различать строку и целое число по их CLR-типу. Однако; Мне нужно поле типа для других целей (более одного типа с ограниченным значением может быть представлено одним типом CLR).
прямо вперед подход:
public struct MyValue
{
private object _value;
private MyValueType _type;
public string String
{
get
{
// todo: check type
return (string)_value;
}
set
{
// todo: validate value
_type = MyValueType.String;
_value = value;
}
}
public long Int64
{
get
{
// todo: check type
return (long)_value;
}
set
{
// todo: validate value
_type = MyValueType.Int64;
_value = value;
}
}
}
Однако такой подход требует некоторых «лишние» инструкции IL:
- Стринг-добытчик нужно
castclass
инструкцию отлиты из объекта в строку. - Для использования Int64-getter требуется команда
unbox.any
для перевода с объекта на длинные. - Для установки Int64-setter требуется команда
box
для переноса от объекта long к объекту.
Цель этой структуры заключается в обеспечении соблюдения ограничений, поэтому к моменту ее получения или установки значения известно, что она будет иметь правильный тип.
Поэтому я рассматриваю использование атрибутов FieldOffset. Что-то вроде этого:
[StructLayout(LayoutKind.Explicit)]
public struct MyValue
{
[FieldOffset(0)]
private string _string;
[FieldOffset(0)]
private long _int64;
[FieldOffset(8)]
private MyValueType _type;
public string String
{
get
{
// todo: check type
return _string;
}
set
{
// todo: validate value
_type = MyValueType.String;
_string = value;
}
}
public long Int64
{
get
{
// todo: check type
return _int64;
}
set
{
// todo: validate value
_type = MyValueType.Int64;
_int64 = value;
}
}
}
При таком подходе нет дополнительных инструкций по коробке, распаковке или литье. Это заставляет меня думать, что этот подход лучше.
Вопрос: Есть ли недостатки в использовании разметки структуры структуры и атрибутов смещения поля?
Возможно, JIT-компилятор может задохнуться от этого по какой-то причине?
В реальном коде; структура была бы неизменной. Поля будут доступны только для чтения, и у него не будет никаких сеттеров.
Во-первых, я не думал, что это будет иметь значение, так как это будет более или менее означать, что сеттеры будут перемещаться в конструкторы, по одному для каждого типа значений.
Однако; компилятор требует, чтобы все элементы были инициализированы конструктором –, независимо от того, что они имеют одинаковое смещение поля.
мне нужно сделать что-то вроде этого:
public MyValue(string value)
{
// todo: validate value
_int64 = 0; // just to satisfy the compiler
_string = value;
_type = MyValueType.String;
}
public MyValue(long value)
{
// todo: validate value
_string = null; // just to satisfy the compiler
_int64 = value;
_type = MyValueType.Int64;
}
Это означает, что второй подход требует «лишней» инструкции IL тоже. Существует три дополнительных инструкции для «по умолчанию» каждого поля, которое не будет использоваться.
Например: _string = null
ldarg.0
, ldnull
и stfld
.
Эти дополнительные инструкции являются полностью расточительными. И будет хуже, если я добавлю дополнительные поля.
So; вопрос также: Будет ли JIT-компилятор достаточно умен, чтобы игнорировать эти расточительные инструкции?
Какая проблема будет решена? –
@ErikPhilips: Если вы имеете в виду сам тип, это обеспечит общий способ работы с этими ограниченными значениями. Что важно в этом случае. Если вы имеете в виду возиться с инструкциями IL - это больше для удовольствия и потому, что я забочусь о деталях. –
Почему бы не хранить длинное слово в одном поле и строку в другом? – IllidanS4