2010-01-25 20 views
12

Teniendo en cuenta dicha enumeración:Delphi 2010 RTTI: Explora las enumeraciones

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

Dónde XMLName es un atributo personalizado que se utiliza para definir la cadena de serialización para los miembros de esta enumeración.

¿Cómo puedo explorar los atributos asociados a cada miembro de esta enumeración?

Respuesta

14

Los atributos asociados con los elementos en las enumeraciones no se almacenan actualmente en los datos de Win32 RTTI en el ejecutable. RTTI ya es responsable de un aumento justo en el tamaño de los ejecutables, por lo que algunas líneas tuvieron que dibujarse en alguna parte. Los atributos en Delphi Win32 son compatibles con tipos, campos de registros y campos, métodos, sus parámetros y propiedades de clases.

Las declaraciones de atributos no causan errores debido a la compatibilidad con Delphi para .NET.

+8

Good explanati en. Pero IMO en ese caso, deberían causar una advertencia de "función de idioma no admitida", como lo hace el otro atributo de atributo no válido. –

3

Estos es una buena descripción de RTTI en Delphi 2010 en la web: http://robstechcorner.blogspot.com/2009/09/so-what-is-rtti-rtti-is-acronym-for-run.html

Usted puede obtener los valores de la enumeración y realizar copias de los ordinales utilizando las funciones de RTTI "viejo" en la unidad de TypInfo (GetEnumValue, GetEnumName). Y recorte las letras minúsculas para obtener el mismo resultado que el anterior, pero no es tan flexible.

17

Mientras que Barry respondió claramente su pregunta con respecto a los atributos en los elementos enum, voy a echar un vistazo a otra sugerencia. De su ejemplo, está prefijando cada elemento enum con 'tod' como es tradicional en Delphi porque los elementos enum son de alcance global (es decir, si tuviera un identificador todABC en el alcance además de los elementos todABC enum, podría obtener algunos comportamientos extraños).

A partir de D2007, introdujimos la noción de "enums con ámbito" que, cuando está habilitada, requiere que califique el elemento enum con el identificador de la enumeración en sí. Por ejemplo:

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

Te solicitarán que hagas referencia al elemento ABC como TTypeOfData.ABC. Esto le permite usar identificadores de elemento enum sin prefijo y no corre el riesgo de tener conflictos ya que los elementos tienen "alcance" en la enumeración. Cualquier enum declarada mientras {$ SCOPEDENUMS} está habilitado se comportará de esta manera.

Dado que ahora puede usar el RTTI de forma segura para obtener los nombres de los elementos de enumeración reales en el formato que desee.

+0

Gracias Allen, Ese fue un mal ejemplo. Mi enumeración es un poco más compleja y las cadenas serializadas no son lo mismo que los miembros de la enumeración. – ZeDalaye

+0

¡Uf, eso es genial! No sabía eso, siempre me disgustó el desorden global frente a los compromisos de los prefijos feos que uno tuvo que hacer con la enumeración ... –

+0

@ Zedalaye, Sospeché que, sin embargo, si hay alguna pizca de utilidad en mi sugerencia ... Si no, estoy seguro de que alguien puede encontrarlo útil. –

1

Para aquellos que están interrested en una solución práctica a este problema, he resuelto de esa manera:

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; 

Y más adelante, en el código de serialización, utilizo RTTI para buscar una función de clase conventionnaly llamado AsString y llámelo con la propiedad 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

Esta es una solución razonable. Me gusta mucho la sugerencia de Allen Bauer también. –

+0

¿Para qué es el '&' in 'property & Type'? – Shannon

+0

Tipo es una palabra reservada en Delphi. Prefijo con & hace posible usar esa palabra como un identificador. – ZeDalaye

3

Ok, creo que he encontrado una solución mejor. Declaro un nuevo tipo de atributo, p.:

TEnumAttribute = class (TCustomAttribute) 
    private 
    FCaption : string; 
    public 
    constructor Create (const Caption : string); 
    property Caption : string read FCaption write FCaption; 
end; 

Ahora añadir atributos a mi enumeración:

[TEnumAttribute ('Normal')] 
[TEnumAttribute ('High')] 
TExampleEnum = (eeNormal,eeHigh); 

Ahora es fácil acceder a los atributos por su ordinal:

RttiType := RttiContext.FindType ('ExampleUnit.TExampleEnum'); 
RttiAttributes := Rttitype.GetAttributes; 
Test := TEnumAttributes(RttiAttributes[index]).Caption; 
+0

genial !! la respuesta más cercana a esta pregunta ... –

0

que uso y array de cadenas en el sección const:

type 
    TTypeOfData = (
    todABC, 
    todDEF, 
    todGHI 
); 

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