Delphi 2010 RTTI: изучить перечисления - PullRequest
13 голосов
/ 25 января 2010

С учетом такого перечисления:

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

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

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

Ответы [ 6 ]

18 голосов
/ 26 января 2010

Хотя Барри четко ответил на ваш вопрос, касающийся атрибутов элементов enum, я попробую еще одно предложение. Из вашего примера вы добавляете к каждому элементу enum префикс «tod», как это принято в Delphi, поскольку элементы enum имеют глобальную область видимости (т. Е. Если бы у вас был идентификатор todABC в области действия в дополнение к элементам перечисления todABC, вы можете получить странное поведение).

Начиная с D2007, мы ввели понятие «enum с ограниченным диапазоном», которое, когда включено, требует от вас квалифицировать элемент enum с идентификатором самого enum. Например:

{$SCOPEDENUMS ON}
type
  TTypeOfData = (ABC,DEF,GHI);

Требует, чтобы вы ссылались на элемент ABC как TTypeOfData.ABC. Это позволяет вам использовать не префиксные идентификаторы элементов перечисления и не подвергать риску конфликтов, так как элементы «ограничены» для перечисления. Любое перечисление, объявленное при включенном {$ SCOPEDENUMS}, будет вести себя следующим образом.

Учитывая это, теперь вы можете безопасно использовать RTTI для получения фактических имен элементов enum в нужном вам формате.

14 голосов
/ 25 января 2010

Атрибуты, связанные с элементами в перечислениях, в настоящее время не сохраняются в данных RTTI Win32 в исполняемом файле. RTTI уже несет ответственность за значительное увеличение размера исполняемых файлов, поэтому некоторые линии нужно было где-то рисовать. Атрибуты в Delphi Win32 поддерживаются для типов, полей записей, а также полей, методов, их параметров и свойств классов.

Объявления атрибутов не вызывают ошибок из-за обратной совместимости с Delphi для .NET.

3 голосов
/ 12 апреля 2010

Хорошо, я думаю, что нашел лучшее решение. Я объявляю новый тип атрибута, например ::10000

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;
3 голосов
/ 25 января 2010

Это хороший обзор RTTI в Delphi 2010 в Интернете: http://robstechcorner.blogspot.com/2009/09/so-what-is-rtti-rtti-is-acronym-for-run.html

Вы можете получить значения перечисления и вернуть порядковые номера, используя функции "OLD" RTTI в модуле TypInfo (GetEnumValueGetEnumName).И обрезая строчные буквы, вы получаете тот же результат, что и выше, но он не такой гибкий.

1 голос
/ 26 января 2010

Для тех, кто интересуется практическим решением этой проблемы, я решил это так:

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 для поиска функции класса, обычно называемой 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;
0 голосов
/ 18 декабря 2012

Я использую и массив строк в разделе const:

type
  TTypeOfData = (
    todABC,
    todDEF,  
    todGHI
  );

const
  TypeOfDataText: array[TTypeOfData] of string = (
    'ABC',
    'DEF',
    'GHI'
  );
...