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'
  );