Почему я получаю ошибку типа "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 для типов перечислений.
Ответы
Ответ 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;