2011-12-30 3 views
1

Je veux dupliquer une classe. Il suffit que je copie toutes les propriétés de cette classe. Est-il possible de:Comment copier les propriétés d'une instance de classe vers une autre instance de la même classe?

  1. boucle à travers toutes les propriétés d'une classe?
  2. affecter chaque propriété à l'autre propriété, comme a.prop := b.prop?

Les getters et setters doivent prendre en compte les détails d'implémentation sous-jacents. Comme François l'a fait remarquer, je n'ai pas suffisamment formulé ma question. J'espère que le nouveau libellé de la question est mieux

SOLUTION: Linas a obtenu la bonne solution. Trouvez un petit programme de démonstration ci-dessous. Les classes dérivées fonctionnent comme prévu. Je ne connaissais pas les nouvelles possibilités RTTI jusqu'à ce que plusieurs personnes me l'aient indiqué. Informations très utiles Merci à tous.

unit properties; 

    interface 

    uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
     Dialogs, StdCtrls, 
     RTTI, TypInfo; 

    type 
    TForm1 = class(TForm) 
     Memo1: TMemo; 
     Button0: TButton; 
     Button1: TButton; 

     procedure Button0Click(Sender: TObject); 
     procedure Button1Click(Sender: TObject); 

    public 
     procedure GetObjectProperties (AObject: TObject; AList: TStrings); 
     procedure CopyObject<T: class>(ASourceObject, ATargetObject: T); 
    end; 

    TDemo = class (TObject) 
    private 
     FIntField: Int32; 

     function get_str_field: string; 
     procedure set_str_field (value: string); 

    public 
     constructor Create; virtual; 

     property IntField: Int32 read FIntField write FIntField; 
     property StrField: string read get_str_field write set_str_field; 
    end; // Class: TDemo // 

    TDerived = class (TDemo) 
    private 
     FList: TStringList; 

     function get_items: string; 
     procedure set_items (value: string); 

    public 
     constructor Create; override; 
     destructor Destroy; override; 
     procedure add_string (text: string); 

     property Items: string read get_items write set_items; 
    end; 

    var Form1: TForm1; 

    implementation 

    {$R *.dfm} 

    procedure TForm1.GetObjectProperties(AObject: TObject; AList: TStrings); 
    var ctx: TRttiContext; 
     rType: TRttiType; 
     rProp: TRttiProperty; 
     AValue: TValue; 
     sVal: string; 

    const SKIP_PROP_TYPES = [tkUnknown, tkInterface]; 

    begin 
    if not Assigned(AObject) and not Assigned(AList) then Exit; 

    ctx := TRttiContext.Create; 
    rType := ctx.GetType(AObject.ClassInfo); 
    for rProp in rType.GetProperties do 
    begin 
     if (rProp.IsReadable) and not (rProp.PropertyType.TypeKind in SKIP_PROP_TYPES) then 
     begin 
      AValue := rProp.GetValue(AObject); 
      if AValue.IsEmpty then 
      begin 
       sVal := 'nil'; 
      end else 
      begin 
       if AValue.Kind in [tkUString, tkString, tkWString, tkChar, tkWChar] 
       then sVal := QuotedStr(AValue.ToString) 
       else sVal := AValue.ToString; 
      end; 
      AList.Add(rProp.Name + '=' + sVal); 
     end; 
    end; 
    end; 

    procedure TForm1.CopyObject<T>(ASourceObject, ATargetObject: T); 
    const 
    SKIP_PROP_TYPES = [tkUnknown, tkInterface, tkClass, tkClassRef, tkPointer, tkProcedure]; 
    var 
    ctx: TRttiContext; 
    rType: TRttiType; 
    rProp: TRttiProperty; 
    AValue, ASource, ATarget: TValue; 
    begin 
    Assert(Assigned(ASourceObject) and Assigned(ATargetObject) , 'Both objects must be assigned'); 
    ctx := TRttiContext.Create; 
    rType := ctx.GetType(ASourceObject.ClassInfo); 
    ASource := TValue.From<T>(ASourceObject); 
    ATarget := TValue.From<T>(ATargetObject); 

    for rProp in rType.GetProperties do 
    begin 
     if (rProp.IsReadable) and (rProp.IsWritable) and not (rProp.PropertyType.TypeKind in SKIP_PROP_TYPES) then 
     begin 
     //when copying visual controls you must skip some properties or you will get some exceptions later 
     if SameText(rProp.Name, 'Name') or (SameText(rProp.Name, 'WindowProc')) then 
      Continue; 
     AValue := rProp.GetValue(ASource.AsObject); 
     rProp.SetValue(ATarget.AsObject, AValue); 
     end; 
    end; 
    end; 

    procedure TForm1.Button0Click(Sender: TObject); 
    var demo1, demo2: TDemo; 
    begin 
    demo1 := TDemo.Create; 
    demo2 := TDemo.Create; 
    demo1.StrField := '1023'; 

    Memo1.Lines.Add ('---Demo1---'); 
    GetObjectProperties (demo1, Memo1.Lines); 
    CopyObject<TDemo> (demo1, demo2); 

    Memo1.Lines.Add ('---Demo2---'); 
    GetObjectProperties (demo2, Memo1.Lines); 
    end; 

    procedure TForm1.Button1Click(Sender: TObject); 
    var derivate1, derivate2: TDerived; 
    begin 
    derivate1 := TDerived.Create; 
    derivate2 := TDerived.Create; 
    derivate1.IntField := 432; 
    derivate1.add_string ('ien'); 
    derivate1.add_string ('twa'); 
    derivate1.add_string ('drei'); 
    derivate1.add_string ('fjour'); 

    Memo1.Lines.Add ('---derivate1---'); 
    GetObjectProperties (derivate1, Memo1.Lines); 
    CopyObject<TDerived> (derivate1, derivate2); 

    Memo1.Lines.Add ('---derivate2---'); 
    GetObjectProperties (derivate2, Memo1.Lines); 
    end; 

    constructor TDemo.Create; 
    begin 
    IntField := 321; 
    end; // Create // 

    function TDemo.get_str_field: string; 
    begin 
    Result := IntToStr (IntField); 
    end; // get_str_field // 

    procedure TDemo.set_str_field (value: string); 
    begin 
    IntField := StrToInt (value); 
    end; // set_str_field // 

    constructor TDerived.Create; 
    begin 
    inherited Create; 

    FList := TStringList.Create; 
    end; // Create // 

    destructor TDerived.Destroy; 
    begin 
    FList.Free; 

    inherited Destroy; 
    end; // Destroy // 

    procedure TDerived.add_string (text: string); 
    begin 
    FList.Add (text); 
    end; // add_string // 

    function TDerived.get_items: string; 
    begin 
    Result := FList.Text; 
    end; // get_items // 

    procedure TDerived.set_items (value: string); 
    begin 
    FList.Text := value; 
    end; // set_items // 

    end. // Unit: properties // 
+0

Qu'est-ce que Delphi version utilisez-vous? – Linas

+1

Il y a une question récente qui est similaire à la vôtre, les réponses utilisent "new RTTI" et nécessitent donc Delphi version 2010 ou ultérieure, voir http://stackoverflow.com/q/8679735/723693 – ain

+0

Voir ce que vous avez codé jusqu'à présent . C'est une entrée essentielle. Votre question est encore vague: p. ces propriétés sont-elles publiées? la classe est-elle descendante de TPersistent? quelle version du compilateur? – menjaraz

Répondre

4

Essayez ce code (mais je ne conseille copie des propriétés des composants visuels, car vous aurez besoin de sauter manuellement certaines propriétés):

uses 
    Rtti, TypInfo; 

procedure CopyObject<T: class>(ASourceObject, ATargetObject: T); 

procedure TForm1.CopyObject<T>(ASourceObject, ATargetObject: T); 
const 
    SKIP_PROP_TYPES = [tkUnknown, tkInterface, tkClass, tkClassRef, tkPointer, tkProcedure]; 
var 
    ctx: TRttiContext; 
    rType: TRttiType; 
    rProp: TRttiProperty; 
    AValue, ASource, ATarget: TValue; 
begin 
    Assert(Assigned(ASourceObject) and Assigned(ATargetObject) , 'Both objects must be assigned'); 
    ctx := TRttiContext.Create; 
    rType := ctx.GetType(ASourceObject.ClassInfo); 
    ASource := TValue.From<T>(ASourceObject); 
    ATarget := TValue.From<T>(ATargetObject); 

    for rProp in rType.GetProperties do 
    begin 
    if (rProp.IsReadable) and (rProp.IsWritable) and not (rProp.PropertyType.TypeKind in SKIP_PROP_TYPES) then 
    begin 
     //when copying visual controls you must skip some properties or you will get some exceptions later 
     if SameText(rProp.Name, 'Name') or (SameText(rProp.Name, 'WindowProc')) then 
     Continue; 
     AValue := rProp.GetValue(ASource.AsObject); 
     rProp.SetValue(ATarget.AsObject, AValue); 
    end; 
    end; 
end; 

Exemple d'utilisation:

CopyObject<TDemoObj>(FObj1, FObj2); 
+0

J'avais commencé à partir du même code de base qui m'avait énuméré toutes les propriétés mais n'avait pas encore trouvé une solution pour la copie des propriétés. Merci pour votre solution qui fonctionne bien. Je n'aurais pas pensé appliquer des génériques, ce qui en fait une solution intéressante. Je n'ai pas l'intention de copier des objets visuels, juste des objets créés par moi-même, mais merci pour l'avertissement. – Arnold

1

Votre question en tant que telle n'a pas beaucoup de sens pour moi.

Êtes-vous vraiment en train de créer une nouvelle classe en copiant une classe existante?

Ou essayez-vous de faire une copie profonde d'un exemple A d'une classe dans une autre instance B de la même classe? Dans ce cas, voir this discussion about cloning in another SO question.

1

Vous n'avez pas mentionné votre version Delphi, mais voici un bon début. Vous devez explorer le RTTI Delphi qui vous permet d'obtenir des informations de type à l'exécution. Vous devrez itérer votre classe source pour les types, puis fournir une méthode pour assigner chaque type.

About RTTI

Si vous concevez vos propres classes simples, vous pouvez simplement remplacer assign et y faire vos propres missions de propriété.

+0

Veuillez vous abstenir de la dissimulation de lien (qui inclut aussi le cadrage d'about.com), merci! – OnTheFly

Questions connexes