Entwickler-Ecke

Sonstiges (Delphi) - Problem mit Attributen


OlafSt - Do 13.02.20 17:54
Titel: Problem mit Attributen
Hallo Freunde,

ich pack das mal hier rein, ist zwar grafische Oberfläche, aber irgendwie auch nicht ;)

Ich möchte einen selbstgebauten Serializer erstellen. Dieser soll für einige Controls auf einem Formular genau definierte Properties serialisieren und als XML später wegspeichern. Ich dachte, Attribute wären da eine prima Idee.

Also habe ich ein ganz simples Attribut erstellt:


Delphi-Quelltext
1:
2:
3:
type
   ASHSerializerAttribute = class(TCustomAttribute)
   end;


Mit diesem Attribut statte ich nun die spezifischen Controls meines Formulars aus:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
type
  TForm1 = class(TForm)
    [ASHSerializer] CheckBox1: TCheckBox;
    [ASHSerializer] Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;


Im ButtonClick lasse ich dann serialisieren:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
var
   I: Integer;
   TC: TComponent;
begin
     for I := 0 to self.ComponentCount-1 do
     begin
          TC:=self.Components[i];
          SerializeControl(TC);
     end;
end;


Um an das Attribut zu gelangen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
procedure SerializeControl(TheClass: TObject);
var
   LContext: TRttiContext;
   LType: TRttiType;
   LAttr: TCustomAttribute;
   LP: TArray<TCustomAttribute>;
begin
     { Einen neuen Rtti-Kontext erstellen }
     LContext := TRttiContext.Create;

     { Typinformationen für den Typ TSomeType extrahieren }
     LType := LContext.GetType(TheClass.ClassInfo);
     { Nach dem benutzerdefinierten Attribut suchen und Verarbeitung ausführen }
     LP:=LType.GetAttributes();
     for LAttr in LP do
     begin
          if LAttr is ASHSerializerAttribute then
          begin
               MessageDlg('Gotcha !', mtInformation,[mbOK],0);
          end;
     end;
     { Kontext freigeben }
     LContext.Free;
end;


Compiliert alles, läuft alles. Nur kommt nie ein "Gotcha !" hoch.

Wo liegt mein Denkfehler ?


jaenicke - Do 13.02.20 19:02

Nicht die Klasseninstanz innerhalb der Eigenschaft, also die Controls, haben das Attribut, sondern die Eigenschaften CheckBox1 und Button1! Du musst also durch die PropertiesFelder des Formulars iterieren um die Attribute abzufragen.

Du kannst ja beliebige Objekte in die Eigenschaft hineinpacken und auch wieder herausnehmen. Wo sollte die Information über das Attribut also stehen? Das Objekt weiß ja gar nicht, ob es gerade in der Property steht oder nicht. Die Attribute werden beim Kompilieren verarbeitet.


OlafSt - Mo 17.02.20 14:27

Okay, das habe ich nun erfolglos versucht:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
procedure SerializeControl(TheClass: TObject);
var
   LContext: TRttiContext;
   LType: TRttiType;
   LAttr: TCustomAttribute;
   LP: TArray<TCustomAttribute>;
   LProps: TArray<TRttiProperty>;
   LProp: TRttiProperty;
begin
     { Einen neuen Rtti-Kontext erstellen }
     LContext := TRttiContext.Create;

     { Typinformationen für den Typ TSomeType extrahieren }
     LType := LContext.GetType(TheClass.ClassInfo);
     {Properties abholen}
     LProps:=LType.GetProperties();
     {Alle Properties durchlaufen}
     for LProp in LProps do
     begin
          {Irgendwann müssten wir eine CheckBox finden}
          if LProp.Name.Contains('CheckBox'then
          begin
               LP:=LProp.GetAttributes();  //Ein Breakpoint hier wird nie erreicht
               { Nach dem benutzerdefinierten Attribut suchen und Verarbeitung ausführen }
               //LP:=LType.GetAttributes();
               for LAttr in LP do
               begin
                    if LAttr is ASHSerializerAttribute then
                    begin
                         MessageDlg('Serializable CheckBox', mtInformation,[mbOK],0);
                    end;
               end;
          end;
     end;
     { Kontext freigeben }
     LContext.Free;
end;


Der Aufruf wurde umgestellt auf Serialize(Form1). Eine Checkbox wird allerdings nie gefunden und auch ohne die Contains-Abfrage erreiche ich nie die MessageBox.

Ich bin wohl zu blöd für sowas. Und das nach 25 Jahren Delphi...


jaenicke - Mo 17.02.20 14:34

Entschuldigung, das war mein Fehler. :oops: Das sind ja Felder und nicht Properties.
Mit GetFields statt GetProperties sollte es also genau so gehen wie du es jetzt hast.


OlafSt - Di 18.02.20 10:54

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Entschuldigung, das war mein Fehler. :oops: Das sind ja Felder und nicht Properties.


*kopfklatsch* Der Schlag ins Leere. Hätte ich auch selbst drauf kommen können, als ich bei "Ltype." verharrte und im CodeCompletion die GetFields-Methode sah.

Ich probiere das heute Nachmittag aus. Wäre so megacool, wenn das funktionieren würde.

Funktioniert genau so, wie ich mir das vorgestellt habe. Vielen Dank, user profile iconjaenicke für den Schubser in die richtige Richtung !