Использование значений в Cum enum (размер enum во время компиляции)? - PullRequest
5 голосов
/ 24 февраля 2012

Ярлык, который я часто использую в C, когда имеешь дело со встроенными API (прежде всего, с протоколами связи), позволяет мне редактировать массив enum и корректировать размер всего остального после этого:добавьте новые типы ошибок перед ERROR_TYPE_MAX, затем я могу использовать это значение везде, чтобы указать размер перечисления.

В C #, однако, это не работает как есть:

enum ErrorTypeList {
   errortype1,
   errortype2,
   ...
   errortypeN,
   ERROR_TYPE_MAX
};

int errorcounts[ErrorTypeList.ERROR_TYPE_MAX]; // Create array to hold a counter for each error type

Это использование представляет ошибку Array size cannot be specified in a variable declaration, которая предлагает использовать new.

Определение массива во время выполнения (через new) мой единственный вариант, или есть способ выполнить это без нового, поскольку размер перечисления после компиляции не изменится?

Есть ли альтернатива этому типу шаблона определения размера перечисления, который лучше подходит для c #?

Ответы [ 5 ]

6 голосов
/ 24 февраля 2012

Если вы хотите размер перечисления, вы можете сделать это:

enum Foo { A , B , C , ... , }
...
int[] someArray = new int[ sizeof(Foo) ] ;

Но это не даст вам того, что вы хотите, так как sizeof возвращает размеробъекта в октетах (в данном случае 4, так как перечисление по умолчанию переносит Int32).

Чтобы получить число значений в перечислении,сделайте что-то вроде этого:

enum Foo { A , B , C , ... , }
...
int[] someArray = new int[ Enum.GetValues(typeof(Foo)).Length ] ;

Если вы планируете использовать значение перечисления для индексации в массиве, вы должны заметить, что это делает [необоснованным] предположение, что значения перечисления начинаются с 0и увеличение на 1: последующее изменение определения перечисления, которое нарушает предположение, нарушит все.

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

Было бы лучше использовать словарь, таким образом:

[Flags]
enum Foo { Unknown = 0 , A = 1 , B = 2 , C = 4 , D = 8 , E = 16 , ... , }
...
Dictionary<Foo,SomeType> myDictionary = new Dictionary<Foo,SomeType>() ;

Теперь ваш код стал гораздо более гибким: вы не делаетезаботиться о том, как устроены внутренние элементы перечисления.

Вы можете даже предварительно заполнить словарь ожидаемым набором ключей (и выдать исключение, если передано значение, отличное от ожидаемого).

Отредактировано в примечание:

Вы также можете сделать что-то подобное:

void DoSomething()
{
  int[] someArray = CreateArrayBasedOnFoo<int>() ;
  ...
  return ;
}

public T[] CreateArrayBasedOnFoo<T>()
{
  Foo[] values = (Foo[]) Enum.GetValues(typeof(Foo)) ;
  int   lo     = (int) values.Min() ;
  int   hi     = (int) values.Max() ;
  int  domain  = ( hi - lo ) + 1 ;
  T[] instance = (T[]) Array.CreateInstance( typeof(T), new int[]{domain} , new int[]{lo} ) ;

  return instance ;
}

Метод CreateArrayBasedOnFoo<T>() вернет массив, размер которого соответствует размеру перечисления, и нижняя граница которого является наименьшим значением изENUM.Например, если у вас есть 3 дискретных значения: 3, 5 и 7, массив, который будет возвращен, будет иметь длину 5 элементов и будет иметь нижнюю границу 3.

5 голосов
/ 24 февраля 2012

Вам придется использовать new.

Объявление переменной / поля не определяет размер массива, это достигается только при создании экземпляра массива.

Самое большее, вы можете указать, сколько измерений будет иметь массив, но не размер каждого измерения.

2 голосов
/ 24 февраля 2012

Чтобы ответить на вторую часть вашего вопроса, вы можете получить количество записей в вашем enum, используя класс Enum, например:

enum ErrorTypeList {
   errortype1,
   errortype2,
   ...
   errortypeN // ERROR_TYPE_MAX is not necessary
};


int errorCounts[] = new int[Enum.GetNames(typeof(ErrorTypeList)).Length];

Обратите внимание, что трюк со счетчикамив массиве работает только тогда, когда значения enum являются последовательными.Это несколько более хрупко, чем Dictionary<ErrorTypeList,int>, что сработает, даже если вы решите присвоить константы перечисления непоследовательные или отрицательные значения.Конечно, вам нужно было бы инициализировать счетчики нулями в начале, чтобы избежать исключения «элемент отсутствует» во время выполнения.

2 голосов
/ 24 февраля 2012

Вы можете определять массивы фиксированной длины только в unsafe struct.В противном случае C # поддерживает создание массивов только во время выполнения с использованием new.

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

0 голосов
/ 24 февраля 2012

Из-за инициализаторов записей enum я считаю небезопасным использование числа записей enum для проверки границ значения enum в C #.

Существует множество методов проверки, таких как Enum.IsDefined () и Enum.ToObject (), которые позволяют более безопасно проверять и преобразовывать числовые значения в типовые записи перечисления.

См. Ссылку на MSDN для класса Enum:

http://msdn.microsoft.com/en-us/library/system.enum.aspx

Отредактировано для уточнения формулировки проблемы ...

Я думаю, что словарь, отображающий значения перечисления и целочисленные счетчики, будет лучше соответствовать вашим потребностям. Он все равно будет работать, если кто-то прикрепит инициализатор к одной или нескольким вашим записям enum.

enum MyEnum {a=32, b, c, d=48, e, f}

Dictionary<MyEnum, int>ErrorCounts = new Dictionary<MyEnum, int>();

и если вы действительно хотите предварительно инициализировать его:

foreach(MyEnum me in Enum.GetValues(typeof(MyEnum)))
{
    ErrorCounts[me] = 0;
}

в противном случае, я бы, вероятно, добавил записи только при возникновении ошибки

private void IncrementErrorCount(MyEnum val)
{
    if(ErrorCounts[val] == null)
        ErrorCounts[val]=1
    else
        ErrorCounts[val]++;
}
...