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

Delphi 2010 RTTI: Исследуйте перечисления

Учитывая такое перечисление:

type
  TTypeOfData = (
    [XmlName('ABC')] todABC,
    [XmlName('DEF')] todDEF,  
    [XmlName('GHI')] todGHI
  );

Где XmlName - это настраиваемый атрибут, используемый для определения строки сериализации для членов этой переписи.

Как я могу изучить атрибуты, связанные с каждым членом этого перечисления?

4b9b3361

Ответ 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'
  );