Подтвердить что ты не робот

Почему я получаю ошибку типа "type not typeinfo" с типом перечисления

Я объявил следующий тип перечисления, в котором я хочу, чтобы первый член имел порядковое значение 1 (один), а не обычный 0 (ноль):

  type
    TMyEnum = (
               meFirstValue = 1,
               meSecondValue,
               meThirdValue
              );

Если я вызываю TypeInfo(), например. как часть вызова GetEnumName(), я получаю ошибку компилятора:

  GetEnumName(TypeInfo(TMyEnum), Ord(aValue));

ОШИБКА: "E2134: Тип 'TMyEnum' не имеет типаinfo

Почему это?

Я знаю, что классы имеют только типinfo, если они скомпилированы с параметром компилятора $M, включенным (или производным от какого-то класса, который был, например TPersistent), но я не думал, что существуют особые условия для typeinfo для типов перечислений.

4b9b3361

Ответ 1

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

Если конкретные значения необходимы или желательны, "неиспользуемые" члены перечисления должны быть вставлены в "pad" перечисление по мере необходимости. например (дополнительный отступ только для выделения):

  type
    TMyEnum = (
                meNOTUSED1,   {= 0}
               meFirstValue,  {= 1} 
               meSecondValue,
               meThirdValue
              );

Затем поддиапазон можно использовать для "фильтрации" неиспользуемого начального значения:

   TValidMyEnum = meFirstValue..meThirdValue;

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

Поддиапазон недостаточно, если перечисление содержит "зазоры":

  type
    TMyEnum = (
                meNOTUSED1,   {= 0}
               meFirstValue,  {= 1} 
               meSecondValue,
               meThirdValue,
                meNOTUSED2,
               meFinalValue   {= 5}
              );

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

  type
    TMyEnums = set of TMyEnum;

  const
    meNOTUSED      = [meUNUSED1, meUNUSED2]; //  .. etc as required
    meValidValues  = [Low(TMyEnum)..High(TMyEnum)] - meNOTUSED;


  if NOT (aValue in meValidValues) then
     // etc

Ответ 2

Несмежные перечисления и перечисления, которые не начинаются с нуля, не имеют typeinfo. Для реализации typeinfo он должен быть в другом формате, чем существующий tkEnumeration, из-за проблем обратной совместимости.

Я подумывал о реализации tkDiscontiguousEnumeration (или, возможно, лучше названного члена) для Delphi 2010, но выгода казалась небольшой, учитывая их относительную нехватку и трудности с перечислением - как вы эффективно кодируете диапазоны? Некоторые кодировки лучше для одних сценариев, хуже для других.

Ответ 3

Когда вы хотите преобразовать перечисления в конкретные значения (и обратно), я обычно создаю массив const с желаемыми значениями для каждого значения перечисления:

Const MyEnumValues: array[TMyEnum] of integer = (1,2,5);

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

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

Чтобы получить значение for для значений перечисления, просто напишите:

Value := MyEnumValues[myenum];

А чтобы получить значение перечисления на основе значения just, просто зациклите значения MyEnumValues:

Function GetEnumByValue(value:integer): TMyEnum;
Var
  myenum: TMyEnum;
Begin
  For myenum = low(TMyEnum) to high(TMyEnum) do
    If MyEnumValues[myenum] = value then
      exit(myenum);
  Raise exception.create(‘invalid value for tmyenum);
End;