2010-01-25 47 views
12

考虑到这样一个枚举:德尔福2010 RTTI:探索枚举

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

哪里XmlName是用来定义此枚举的成员序列化字符串自定义属性。

我该如何探索附属于此枚举每个成员的属性?

回答

14

与枚举中的元素相关联的属性当前未存储在可执行文件中的Win32 RTTI数据中。 RTTI已经负责公平地增加可执行文件的大小,因此必须在某处绘制一些行。 Delphi Win32中的属性支持类型,记录字段,字段,方法,它们的参数和类的属性。

属性声明不会因为与Delphi for .NET的向后兼容性而导致错误。

+8

良好的讲解上。但在这种情况下,IMO应该引起“不支持的语言功能”警告,就像其他无效属性一样。 – 2010-01-25 20:11:26

17

虽然巴里明确地回答了你关于枚举元素的属性的问题,但我会采取另一个暗示。在你的例子中,你使用'tod'作为每个枚举元素的前缀,因为在Delphi中是传统的,因为枚举元素是全局的(即,如果除了todABC枚举元素之外,你还有一个标识符todABC,奇怪的行为)。

从D2007开始,我们引入了“范围枚举”的概念,它在启用时要求您使用枚举本身的标识符限定枚举元素。例如:

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

将要求您将ABC元素称为TTypeOfData.ABC。这使您可以使用不带前缀的枚举元素标识符,并且不会因为元素被“范围限定”为枚举而发生冲突。在{$ SCOPEDENUMS}启用时声明的任何枚举都将以这种方式运行。

鉴于此,您现在可以安全地使用RTTI以您希望的格式获取实际的枚举元素名称。

+0

谢谢艾伦, 这是一个坏榜样。我的枚举有点复杂,并且序列化的字符串与枚举成员不同。 – ZeDalaye 2010-01-26 07:35:09

+0

唷,那很酷!不知道,总是不喜欢全球混乱与丑陋的前缀妥协,必须使用枚举... – 2010-01-26 13:38:25

+0

@ZeDalaye, 我怀疑,但是,如果有一些有用的金块在我的建议...如果不,我相信有人会觉得它有用。 – 2010-01-26 17:20:58

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并与物业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; 
+1

这是一个合理的解决方案。我也非常喜欢艾伦鲍尔的建议。 – 2010-08-07 23:10:49

+0

“属性和类型”中的“&”是什么? – Shannon 2013-03-27 03:07:05

+0

类型是Delphi中的保留字。用&前缀并使得可以将该单词用作标识符。 – ZeDalaye 2013-04-05 13:24:46

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

太棒了!这个问题最接近的答案.. – 2012-08-06 16:04:06

0

和字符串数组常量部分:

type 
    TTypeOfData = (
    todABC, 
    todDEF, 
    todGHI 
); 

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