Delphi 2010 RTTI: изучение перечислений
учитывая такое перечисление :
type
TTypeOfData = (
[XmlName('ABC')] todABC,
[XmlName('DEF')] todDEF,
[XmlName('GHI')] todGHI
);
где XmlName-пользовательский атрибут, используемый для определения строки сериализации для членов этого перечисления.
Как я могу исследовать атрибуты, прикрепленные к каждому члену этого перечисления ?
6 ответов
атрибуты, связанные с элементами перечислений, в настоящее время не хранятся в данных Win32 RTTI в исполняемом файле. RTTI уже несет ответственность за справедливое увеличение размера исполняемых файлов, поэтому некоторые линии должны были быть где-то нарисованы. Атрибуты в Delphi Win32 поддерживаются для типов, полей записей и полей, методов, их параметров и свойств классов.
объявления атрибутов не вызывают ошибок из-за обратной совместимости с Delphi для - ... Сеть.
в то время как Барри четко ответил на ваш вопрос относительно атрибутов элементов перечисления, я попробую другое предложение. Из вашего примера вы добавляете префикс каждого элемента перечисления с "tod", как это традиционно в Delphi, потому что элементы перечисления являются глобальными по области (т. е. если у вас есть идентификатор todABC в области в дополнение к элементам перечисления todABC, вы можете получить некоторые странные поведения).
начиная с D2007, мы ввели понятие "перечисления области", которое, когда включено, требуется определить элемент enum идентификатор перечисления. Например:
{$SCOPEDENUMS ON}
type
TTypeOfData = (ABC,DEF,GHI);
потребует, чтобы вы ссылались на элемент ABC как TTypeOfData.АЗБУКА. Это позволяет использовать идентификаторы элементов перечисления без префиксов и не рисковать конфликтами, поскольку элементы "ограничены" перечислением. Любое перечисление, объявленное в то время как {$SCOPEDENUMS} включено, будет вести себя таким образом.
учитывая это, теперь вы можете безопасно использовать RTTI для получения фактического перечисление имен элементов в формате, который вы хотите.
Это хороший обзор RTTI в Delphi 2010 в интернете: http://robstechcorner.blogspot.com/2009/09/so-what-is-rtti-rtti-is-acronym-for-run.html
вы можете получить значения перечисления и вернуть ординалы, используя "старые" функции RTTI в unit TypInfo (GetEnumValue, GetEnumName). И обрезать строчные буквы вы получаете тот же результат, что и выше, но он не так гибок.
хорошо, я думаю, что нашел лучшее решение. Я объявляю новый тип атрибута, например:
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;
для тех, кто заинтересован в практическом решении этой проблемы, я решил ее таким образом:
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;
Я использую и массив строк в разделе const:
type
TTypeOfData = (
todABC,
todDEF,
todGHI
);
const
TypeOfDataText: array[TTypeOfData] of string = (
'ABC',
'DEF',
'GHI'
);