Delphi 2010 RTTI: Исследуйте перечисления
Учитывая такое перечисление:
type
TTypeOfData = (
[XmlName('ABC')] todABC,
[XmlName('DEF')] todDEF,
[XmlName('GHI')] todGHI
);
Где XmlName - это настраиваемый атрибут, используемый для определения строки сериализации для членов этой переписи.
Как я могу изучить атрибуты, связанные с каждым членом этого перечисления?
Ответы
Ответ 1
Атрибуты, связанные с элементами в перечислениях, в настоящее время не хранятся в данных Win32 RTTI в исполняемом файле. RTTI уже несет ответственность за справедливое увеличение размера исполняемых файлов, поэтому некоторые строки нужно было нарисовать где-то. Атрибуты в Delphi Win32 поддерживаются типами, полями записей и полями, методами, их параметрами и свойствами классов.
Объявления атрибутов не вызывают ошибок из-за обратной совместимости с Delphi для .NET.
Ответ 2
В то время как Барри четко ответил на ваш вопрос относительно атрибутов элементов перечисления, я сделаю удар по другому предложению. В вашем примере вы префикс каждого элемента enum с помощью "tod", как это принято в Delphi, поскольку элементы перечисления являются глобальными по объему (т.е. Если в дополнение к элементам перечисления todABC у вас есть идентификатор todABC в области видимости, вы можете получить некоторые нечетное поведение).
Начиная с D2007, мы ввели понятие "scoped enums", которое, когда включено, требует, чтобы вы квалифицировали элемент перечисления с идентификатором самого перечисления. Например:
{$SCOPEDENUMS ON}
type
TTypeOfData = (ABC,DEF,GHI);
Вам потребуется обратиться к элементу ABC как TTypeOfData.ABC. Это позволяет использовать идентификаторы элементов с префиксом enum и не запускать риск возникновения конфликтов, поскольку элементы "привязаны" к перечислению. Любое перечисление, объявленное при включенном {$ SCOPEDENUMS}, будет вести себя таким образом.
Учитывая, что теперь вы можете безопасно использовать RTTI для получения фактических имен элементов перечисления в желаемом формате.
Ответ 3
Это хороший обзор RTTI в Delphi 2010 в Интернете: http://robstechcorner.blogspot.com/2009/09/so-what-is-rtti-rtti-is-acronym-for-run.html
Вы можете получить значения перечисления и вернуть ординалы с помощью функций "OLD" RTTI в модуле TypInfo (GetEnumValue, GetEnumName). И скопируйте строчные буквы, чтобы получить тот же результат, что и выше, но он не такой гибкий.
Ответ 4
Хорошо, думаю, я нашел лучшее решение. Я объявляю новый тип атрибута, например:
TEnumAttribute = class (TCustomAttribute)
private
FCaption : string;
public
constructor Create (const Caption : string);
property Caption : string read FCaption write FCaption;
end;
Теперь я добавляю атрибуты к моему перечислению:
[TEnumAttribute ('Normal')]
[TEnumAttribute ('High')]
TExampleEnum = (eeNormal,eeHigh);
Теперь легко получить доступ к атрибутам по порядку:
RttiType := RttiContext.FindType ('ExampleUnit.TExampleEnum');
RttiAttributes := Rttitype.GetAttributes;
Test := TEnumAttributes(RttiAttributes[index]).Caption;
Ответ 5
Для тех, кто интересуется практическим решением этой проблемы, я решил это так:
type
TTypeOfData = (todABC, todDEF, todGHI);
TMySerializableClass = class
private
FType: TTypeOfData;
public
property &Type: TTypeOfData read FType write FType;
class function TypeOfDataAsString(&Type: TTypeOfData): String;
end;
implementation
class function TMySerializableClass.TypeOfDataAsString(&Type: TTypeOfData): String;
const
TYPE_STRING: array[TypeOfDataAsString] of String = ('ABC', 'DEF', 'GHI);
begin
Result := TYPE_STRING[&Type];
end;
И позже, в коде сериализации, я использую RTTI для поиска функции класса conventionnaly с именем AsString и вызываю ее с помощью свойства TValue:
procedure Serialize(const V: TValue);
var
N: String;
T: TRttiType;
F: TRttiField;
M: TRttiMethod;
R: TValue;
begin
case V.TypeInfo^.Kind of
tkEnumeration:
begin
T := Ctx.GetType(TypeInfo(TMySerializableClass));
N := V.TypeInfo.Name + 'AsString';
if N[1] = 'T' then
Delete(N, 1, 1);
M := T.GetMethod(N);
if (M <> nil) and M.IsClassMethod and (M.MethodKind = mkClassFunction) and (M.ReturnType.TypeKind = tkUString) then
begin
R := M.Invoke(TTicket, [V]);
// serialize R.AsString
end;
end;
...
end;
Ответ 6
Я использую и массиву строки в секции const:
type
TTypeOfData = (
todABC,
todDEF,
todGHI
);
const
TypeOfDataText: array[TTypeOfData] of string = (
'ABC',
'DEF',
'GHI'
);