2012-04-17 30 views
11

Tengo una clase en mi aplicación Delphi donde me gustaría una manera fácil y dinámica de restablecer todas las propiedades de cadena a '' y todas las propiedades booleanas a False Hasta donde puedo ver en la web debería ser posible hacer un bucle de algún tipo, pero cómo hacerlo no está claro para mí.Cómo recorrer todas las propiedades en una clase

+4

¿Qué versión delphi estás utilizando? – zz1433

+0

Está bien que lo olvidé - Estoy usando Delphi XE – OZ8HP

Respuesta

10

Tenga en cuenta que el siguiente código sólo funciona para el portal de anuncios de una clase! Además, la instancia de una clase pasada a la función siguiente debe tener al menos una sección publicada definida.

Aquí se explica cómo establecer los valores de propiedad de cadena publicados en una cadena vacía y los valores booleanos en False utilizando el RTTI de estilo antiguo.

Si tiene Delphi anterior a Delphi 2009, es posible que le falte el tipo de tkUString. Si es así, basta con quitar
desde el siguiente código:

uses 
    TypInfo; 

procedure ResetPropertyValues(const AObject: TObject); 
var 
    PropIndex: Integer; 
    PropCount: Integer; 
    PropList: PPropList; 
    PropInfo: PPropInfo; 
const 
    TypeKinds: TTypeKinds = [tkEnumeration, tkString, tkLString, tkWString, 
    tkUString]; 
begin 
    PropCount := GetPropList(AObject.ClassInfo, TypeKinds, nil); 
    GetMem(PropList, PropCount * SizeOf(PPropInfo)); 
    try 
    GetPropList(AObject.ClassInfo, TypeKinds, PropList); 
    for PropIndex := 0 to PropCount - 1 do 
    begin 
     PropInfo := PropList^[PropIndex]; 
     if Assigned(PropInfo^.SetProc) then 
     case PropInfo^.PropType^.Kind of 
     tkString, tkLString, tkUString, tkWString: 
      SetStrProp(AObject, PropInfo, ''); 
     tkEnumeration: 
      if GetTypeData(PropInfo^.PropType^)^.BaseType^ = TypeInfo(Boolean) then 
      SetOrdProp(AObject, PropInfo, 0); 
     end; 
    end; 
    finally 
    FreeMem(PropList); 
    end; 
end; 

Aquí es un código de prueba simple (tenga en cuenta las propiedades deben ser publicados; si no hay propiedades publicadas en la clase, la sección al menos vacía publicada debe ser allí):

type 
    TSampleClass = class(TObject) 
    private 
    FStringProp: string; 
    FBooleanProp: Boolean; 
    published 
    property StringProp: string read FStringProp write FStringProp; 
    property BooleanProp: Boolean read FBooleanProp write FBooleanProp; 
    end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    SampleClass: TSampleClass; 
begin 
    SampleClass := TSampleClass.Create; 
    try 
    SampleClass.StringProp := 'This must be cleared'; 
    SampleClass.BooleanProp := True; 
    ResetPropertyValues(SampleClass); 
    ShowMessage('StringProp = ' + SampleClass.StringProp + sLineBreak + 
     'BooleanProp = ' + BoolToStr(SampleClass.BooleanProp)); 
    finally 
    SampleClass.Free; 
    end; 
end; 
+1

Disculpe por la demora en volver - estado ocupado con otros proyectos :-) – OZ8HP

+0

@David, gracias! Eso es lo que he estado haciendo en mi ['initial revision'] (http://stackoverflow.com/revisions/10189686/1). No sé por qué lo rompí de esa manera ... – TLama

15

Si usted es un usuario de Delphi 2010 (y superior), entonces hay una nueva unidad de RTTI (rtti.pas). puede usarlo para obtener información de tiempo de ejecución sobre su clase y sus propiedades (propiedades públicas de forma predeterminada, pero puede usar la directiva de compilación {$RTTI} para incluir información de campos protegidos y privados). Por ejemplo, tenemos la siguiente clase de prueba con 3 campos públicos (1 booleano y 2 campos de cadena (uno de ellos es de solo lectura)).

TTest = class(TObject) 
     strict private 
     FString1 : string; 
     FString2 : string; 
     FBool : boolean; 
     public 
     constructor Create(); 
     procedure PrintValues(); 

     property String1 : string read FString1 write FString1; 
     property String2 : string read FString2; 
     property BoolProp : boolean read FBool write FBool; 
    end; 

constructor TTest.Create(); 
begin 
    FBool := true; 
    FString1 := 'test1'; 
    FString2 := 'test2'; 
end; 

procedure TTest.PrintValues(); 
begin 
    writeln('string1 : ', FString1); 
    writeln('string2 : ', FString2); 
    writeln('bool: ', BoolToStr(FBool, true)); 
end; 

para enumerar todas las propiedades del objeto y la pusieron a los valores por defecto se puede usar algo como código de abajo. Primero, debe iniciar la estructura TRttiContext (no es necesario, porque es un registro). Entonces deberías obtener información de rtti sobre tu objeto, después de eso puedes recorrer tus propiedades y filtrarlo (omitir las propiedades de solo lectura y otras que no sean booleanas y remuevas). Tener en cuenta que hay pocos tipo de cadenas: tkUString, tkString y otros (miren TTypeKind en typinfo.pas)

TObjectReset = record 
     strict private 
     public 
     class procedure ResetObject(obj : TObject); static; 
    end; 

{ TObjectReset } 

class procedure TObjectReset.ResetObject(obj: TObject); 
var ctx : TRttiContext; 
    rt : TRttiType; 
    prop : TRttiProperty; 
    value : TValue; 
begin 
    ctx := TRttiContext.Create(); 
    try 
     rt := ctx.GetType(obj.ClassType); 

     for prop in rt.GetProperties() do begin 
      if not prop.IsWritable then continue; 

      case prop.PropertyType.TypeKind of 
       tkEnumeration : value := false; 
       tkUString :  value := ''; 
       else continue; 
      end; 
      prop.SetValue(obj, value); 
     end; 
    finally 
     ctx.Free(); 
    end; 
end; 

código simple para probar:

var t : TTest; 
begin 
    t := TTest.Create(); 
    try 
     t.PrintValues(); 
     writeln('reset values'#13#10); 
     TObjectReset.ResetObject(t); 
     t.PrintValues(); 
    finally 
     readln; 
     t.Free(); 
    end; 
end. 

y el resultado es

string1 : test1 
string2 : test2 
bool: True 
reset values 

string1 : 
string2 : test2 
bool: False 

También eche un vistazo a Atributos, es una buena idea marcar las propiedades (que necesita) para reiniciar) con algún atributo, y puede ser con un valor por defecto como:

[ResetTo('my initial value')] 
property MyValue : string read FValue write FValue; 

podrá filtrar únicamente las propiedades Wich están marcados con ResetToAttribute

+0

Seguro que tengo mucha información para trabajar ahora Creo que la solución de Teran será la que voy a experimentar. Pero volveré. – OZ8HP

+0

Reescribí la respuesta porque leí mal su pregunta (pensando que la necesita para los componentes). Sin embargo, para sus propias clases con propiedades distintas de las publicadas, el RTTI antiguo no se puede usar. Así que definitivamente siga de esta manera ;-) – TLama

Cuestiones relacionadas