Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi RTTI - Property - SetValue (https://www.delphipraxis.net/157067-rtti-property-setvalue.html)

stahli 27. Dez 2010 21:52

RTTI - Property - SetValue
 
Ich versuche, RTTI unter XE zu verstehen und zu nutzen.

Ich habe eine
Delphi-Quellcode:
Klasse TTest
mit den Propertys
Delphi-Quellcode:
S: String
und
Delphi-Quellcode:
I: Integer
;

Nun möchte ich mit
Delphi-Quellcode:
SetString(T, 'S', 'Text')
und analog
Delphi-Quellcode:
SetInteger(T, 'I', 100)
Werte zuweisen:

Delphi-Quellcode:
procedure SetString(T: TTest; PropName: String; Value: String);
var
  Context: TRttiContext;
  ClassInfo: TRttiType;
  PropInfo: TRttiProperty;
  PropValue: TValue;
begin
  if not Assigned(T) then
    Exit;

  Context := TRttiContext.Create;
  ClassInfo := Context.GetType(T.ClassType);
  PropInfo := ClassInfo.GetProperty(PropName);

  if Assigned(PropInfo) then
  begin

{
  so könnte man den Wert auslesen:
  PropValue := PropInfo.GetValue(T);
  if PropValue.Kind = tkString then
    Result := PropValue.AsString

  wie kann ich einen Wert schreiben???
  im Grunde brauche ich so etwas wie T.PropertyByName('PropName').AsString := Value
}

  end;
  context.Free;
end;

procedure SetInteger(T: TTest; PropName: String; Value: Integer);
...Variante mit Integer
Die Hilfe erscheint mir unvollständig, da dort doch keine Objekt-Instanz genutzt wird!?

Weiß jemand Rat?

himitsu 28. Dez 2010 00:08

AW: RTTI - Property - SetValue
 
Ich kann mich täuschen, aber LVar siht mir in diesem Beispiel wie die gewünschte Instanz aus und nicht wie eine Klasse.

Björn Ole 28. Dez 2010 00:49

AW: RTTI - Property - SetValue
 
So gehts:
Delphi-Quellcode:
// Testklasse
TTest = class
private
  FStr: string;
public/published
  property Str: string read FStr write FStr;
end;

// Wert schreiben
if Assigned(PropInfo) then
begin
  PropValue := TValue.From(Value);
  PropInfo.SetValue(T, PropValue);
end;

stahli 28. Dez 2010 03:03

AW: RTTI - Property - SetValue
 
@himitsu
Na ja, im SetProperty-Beispiel wird LVar eben nicht verwendet. Das kann ich daher nicht einordnen und nachvollziehen.

@Björn
Danke, das hat geholfen :thumb:

Mein aktuelles Test-Projekt:
Delphi-Quellcode:
// mein Attribut...
type
  AttrOd = class(TCustomAttribute);

// meine Klasse...
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    [AttrOd]
    property Name: String read get_Name write set_Name;
    [AttrOd]
    property Number: Integer read get_Number write set_Number;

// Read/Write propertys
//
// Testweise werden die propertys einfach in eine Stringlist geschrieben und wieder geladen. Diese sieht so aus:
// Name=xxx
// Number=111
procedure TodData.ReadPropValues(const od: Tod);
var
  Context: TRttiContext;
  RttiType: TRttiType;
  PropInfo: TRttiProperty;
  F: Boolean;
  Attr: TCustomAttribute;
  Value: TValue;
  sl: tstringlist;
begin
  if not Assigned(od) then
    Exit;

  sl := tstringlist.Create;
  sl.LoadFromFile('sl.txt');

  Context := TRttiContext.Create;
  RttiType := Context.GetType(od.ClassType);

  if Assigned(RttiType) then
  begin
    for PropInfo in RttiType.GetProperties do
    begin
      F := False;
      for Attr in PropInfo.GetAttributes do
      begin
        if Attr is AttrOd then
          F := True;
      end;
      if F then
      begin
        Value := TValue.Empty;
        case PropInfo.PropertyType.TypeKind of
          tkUnknown:
            ;
          tkInteger:
            Value := TValue.From(StrToInt(sl.Values[PropInfo.Name]));
          tkChar:
            ;
          tkEnumeration:
            ;
          tkFloat:
            ;
          tkString:
            Value := TValue.From(sl.Values[PropInfo.Name]);
          tkSet:
            ;
          tkClass:
            ;
          tkMethod:
            ;
          tkWChar:
            ;
          tkLString:
            ;
          tkWString:
            ;
          tkVariant:
            ;
          tkArray:
            ;
          tkRecord:
            ;
          tkInterface:
            ;
          tkInt64:
            ;
          tkDynArray:
            ;
          tkUString:
            Value := TValue.From(sl.Values[PropInfo.Name]);
          tkClassRef:
            ;
          tkPointer:
            ;
          tkProcedure:
            ;
        end;
        if not Value.IsEmpty then
          PropInfo.SetValue(od, Value);
      end;
    end;
  end;

  sl.Free;

  Context.Free;
end;

procedure TodData.WritePropValues(const od: Tod);
var
  Context: TRttiContext;
  RttiType: TRttiType;
  PropInfo: TRttiProperty;
  F: Boolean;
  Attr: TCustomAttribute;
  Value: TValue;
  sl: tstringlist;
begin
  if not Assigned(od) then
    Exit;

  sl := tstringlist.Create;

  Context := TRttiContext.Create;
  RttiType := Context.GetType(od.ClassType);

  if Assigned(RttiType) then
  begin
    for PropInfo in RttiType.GetProperties do
    begin
      F := False;
      for Attr in PropInfo.GetAttributes do
      begin
        if Attr is AttrOd then
          F := True;
      end;
      if F then
      begin
        Value := PropInfo.GetValue(od);
        case Value.Kind of
          tkUnknown:
            ;
          tkInteger:
            sl.Values[PropInfo.Name] := IntToStr(Value.AsInteger);
          tkChar:
            ;
          tkEnumeration:
            ;
          tkFloat:
            ;
          tkString:
            sl.Values[PropInfo.Name] := Value.AsString;
          tkSet:
            ;
          tkClass:
            ;
          tkMethod:
            ;
          tkWChar:
            ;
          tkLString:
            ;
          tkWString:
            ;
          tkVariant:
            ;
          tkArray:
            ;
          tkRecord:
            ;
          tkInterface:
            ;
          tkInt64:
            ;
          tkDynArray:
            ;
          tkUString:
            sl.Values[PropInfo.Name] := Value.AsString;
          tkClassRef:
            ;
          tkPointer:
            ;
          tkProcedure:
            ;
        end;
      end;
    end;
  end;

  sl.SaveToFile('sl.txt');
  sl.Free;

  Context.Free;
end;
Ich denke, darauf kann man ganz gut aufbauen :)

Das Schöne dabei ist, mit dem Attribut "AttrOd" kann ich alle Propertys "markieren", die gespeichert und geladen werden sollen.
Außerdem können diese public und müssen nicht published sein. (Felder (Variablen) können sogar privat sein.)

Genaueres zeigt Daniel in seinem Video zu RTTI und Attributen in D2010.

s.h.a.r.k 28. Dez 2010 13:22

AW: RTTI - Property - SetValue
 
Das ist wahrlich ein Fehler. Schaut man aber direkt auf der SetValue-Doku-Seite nach, sieht man dass man eine Objektinstanz benötigt.

stahli 28. Dez 2010 14:01

AW: RTTI - Property - SetValue
 
Hallo Armin,
danke, das stimmt.

Deinen Beitrag zum Thema habe ich auch gefunden. Für meine Zwecke ist SetValue soweit ausreichend.

In Verbindung mit Attributen ist RTTI schon sehr interessant.
Wenn man Objekte speichern möchte, ist das ein guter Grund, ggf. auf D2010 bzw. XE upzugraden.

s.h.a.r.k 28. Dez 2010 15:10

AW: RTTI - Property - SetValue
 
Oh ja, das stimmt auf jedenfall. Ebenso, wenn man sich einen eigenes ORM-Framework schreiben will, kann das echt sehr praktisch sein.

stahli 28. Dez 2010 16:36

AW: RTTI - Property - SetValue
 
Ich habe noch ein Problem mit den Aufzählungstypen:

Delphi-Quellcode:
        Value := TValue.Empty;
        case PropInfo.PropertyType.TypeKind of
          tkUnknown:
            ;
          tkInteger:
            Value := TValue.From(StrToIntDef(PropValue, 0));
          tkChar:
            ;
          tkEnumeration:
            Value := TValue.From(TXyz(GetEnumValue(PropInfo.PropertyType.Handle, PropValue))); // <-- HIER
          tkFloat:
            ;
          ...
        end;
        if not Value.IsEmpty then
          PropInfo.SetValue(od, Value);
Ich möchte hier natürlich universelle Aufzählungstypen zuweisen. Mit GetEnumValue erhalte ich den korrekten Ordinalwert.
Wenn hart in Txyz caste, funktioniert auch die Varianten-Zuweisung korrekt. Wenn aber TAbc eingelesen werden soll, funktioniert das natürlich nicht.
Kann ich irgendwie
Delphi-Quellcode:
PropInfo.MYTYPE(GetEnumValue(PropInfo.PropertyType.Handle, PropValue))
oder ähnliches verwenden?
Irgendwo liegt hoffentlich ein Handle auf den Typ TXyz bzw. TAbc ... bitte, bitte, bitte :!:

Oder kann ich SetValue irgendwie zwingen, einen Ordinalwert für Aufzählungstypen anzunehmen?

stahli 28. Dez 2010 19:19

AW: RTTI - Property - SetValue
 
Liste der Anhänge anzeigen (Anzahl: 1)
Sorry, für den Push.
Das Handle ist offenbar schon das richtige (Name=TStateType).
An der Position des Pfeils will ich nun in diesen "Typ" casten.

stahli 28. Dez 2010 20:39

AW: RTTI - Property - SetValue
 
Ich habe hier eine Lösung gefunden.
Delphi-Quellcode:
          tkEnumeration:
            Value := TValue.FromOrdinal(PropInfo.PropertyType.Handle, GetEnumValue(PropInfo.PropertyType.Handle, PropValue));
So funktioniert es. Der Rest sollte dann zu schaffen sein. :)


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:13 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz