2010-01-25 2 views
12

Учитывая такое перечисление:Delphi 2010 RTTI: Explore Перечисление

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

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

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

ответ

14

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

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

+8

Хорошее объяснение на. Но IMO в этом случае они должны вызывать предупреждение «неподдерживаемой языковой функции», как это делает использование других недопустимых атрибутов. –

3

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

Вы можете получить значения перечисления и обратно порядковые с помощью функции RTTI «старый» в блоке TypInfo (GetEnumValue, GetEnumName). И скопируйте строчные буквы, чтобы получить тот же результат, что и выше, но он не такой гибкий.

17

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

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

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

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

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

+0

Спасибо Аллен, Это был плохой пример. Мое перечисление немного сложнее, а сериализованные строки - это не то же самое, что и члены перечисления. – ZeDalaye

+0

Фу, это круто! Не знал этого, всегда не любил компромисс между глобальными беспорядками и уродливыми префиксами, которые нужно было сделать с перечислением ... –

+0

@ZeDalaye, Я подозревал, что, если в моем предложении есть какой-то самородок полезности ... Если не, я уверен, кто-то может найти это полезным. –

1

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

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 и назовите его собственностью TValate:

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; 
+1

Это разумное решение. Мне очень нравится предложение Аллена Бауэра. –

+0

Что означает '&' в 'property & Type' для? – Shannon

+0

Тип зарезервированное слово в Delphi. Префикс & позволяет использовать это слово в качестве идентификатора. – ZeDalaye

3

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

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; 
+0

большой !! самый близкий ответ на этот вопрос .. –

0

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

type 
    TTypeOfData = (
    todABC, 
    todDEF, 
    todGHI 
); 

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