2016-03-12 11 views
3

В определенном контексте мне нужно управлять ограниченными значениями. Для упрощения; давайте просто скажем, что мне нужно ограничить значения либо строкой, либо 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 = nullldarg.0, ldnull и stfld.

Эти дополнительные инструкции являются полностью расточительными. И будет хуже, если я добавлю дополнительные поля.

So; вопрос также: Будет ли JIT-компилятор достаточно умен, чтобы игнорировать эти расточительные инструкции?

+0

Какая проблема будет решена? –

+0

@ErikPhilips: Если вы имеете в виду сам тип, это обеспечит общий способ работы с этими ограниченными значениями. Что важно в этом случае. Если вы имеете в виду возиться с инструкциями IL - это больше для удовольствия и потому, что я забочусь о деталях. –

+0

Почему бы не хранить длинное слово в одном поле и строку в другом? – IllidanS4

ответ

1

Nevermind. Кажется, что это не может быть сделано в любом случае. Попытка загрузки такого типа будет вызывать TypeLoadException о том, что-то вроде

Не удалось загрузить тип «MyValue» от узла «MyAssembly», поскольку он содержит поле объекта по смещению 0, что неправильно выровненной или перекрытой путем поле без объекта.

Таким образом, я не могу иметь поле ссылочного типа с тем же самым смещением, что и поле типа значения.

Конец рассказа.

Я оставляю это Q & A (не удаляя), хотя в случае, если кому-то еще любопытно.

+1

CLR обычно не позволял вам читать реальную ценность ссылки на объект (что могло бы быть сделано таким образом, если бы это сработало), и вы не будете доверять вам достаточно, чтобы позволить вам сделать это, даже если ваши намерения хороши. – IllidanS4