Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Record Property verschachtelt - Schreibzugriff (https://www.delphipraxis.net/207499-record-property-verschachtelt-schreibzugriff.html)

lxo 30. Mär 2021 08:19

Record Property verschachtelt - Schreibzugriff
 
Hallo,

mir ist vorhin aufgefallen, dass man bei verschachtelten Records keine Werte zuweisen kann.
Warum ist das so oder hab ich da irgendwie einen Denkfehler?

Delphi-Quellcode:
  TTestRec1 = record
  public
    IntValue: Integer;
  end;

  TTestRec2 = record // <- Wenn man das Record als Klasse deklariert funktioniert es
  public
    Test1: TTestRec1;
  end;

  TTestObj1 = class( TObject)
  private
    FTest2: TTestRec2;
  public
    property Test2 : TTestRec2 read FTest2 write FTest2;
  end;

...

procedure TForm53.FormCreate(Sender: TObject);
var
  ltest: TTestObj1;
begin
  ltest := TTestObj1.Create;
  ltest.Test2.Test1.IntValue := 10; // <- "Der linken Seite kann nichts zugewiesen werden"
  ltest.FTest2.Test1.IntValue := 10; // <- Funktioniert
end;
Mache ich aus TTestRec2 eine Klasse oder wenn ich statt auf die Property Test2 direkt auf FTest2 zugreife, funktioniert es.
Aber warum kann man da nichts eintragen wenn man die Property nutzt?

lxo 30. Mär 2021 08:41

AW: Record Property verschachtelt - Schreibzugriff
 
Ich hab gerade bemerkt das es ja auch schon direkt bei der ersten Ebene nicht funktioniert.
Aber hab da noch eine andere Möglichkeit gefunden.

Delphi-Quellcode:
  TTestRec1 = record
  public
    IntValue: Integer;
  end;

  TTestObj1 = class( TObject)
  private
    FTest1: TTestRec1;
  public
    property Test1 : TTestRec1 read FTest1 write FTest1;
    property Test1IntValue : Integer read FTest1.IntValue write FTest1.IntValue; // <- So funktioniert es auch [1/2]
  end;

...

procedure TForm53.FormCreate(Sender: TObject);
var
  ltest: TTestObj1;
begin
  ltest := TTestObj1.Create;
  ltest.Test1.IntValue := 10; // <- "Der linken Seite kann nichts zugewiesen werden"
  ltest.Test1IntValue := 10; // <- So funktioniert es auch [2/2]
end;
Mir ist nur nicht ganz klar wieso das nicht funktioniert ein Record als Property zu verwenden und die einzelnen Eigenschaften des Records zuzuweisen.

Uwe Raabe 30. Mär 2021 09:25

AW: Record Property verschachtelt - Schreibzugriff
 
Wenn du ein Record als Property hast, dann greifst du beim Schreiben auf die Record-Felder ja auf den Getter des Properties zu. Dieser gibt dir aber eine Kopie des Records zurück und nicht das Record-Feld in der Klasse. Du änderst dann eben auch nie diese Kopie und nicht das Original.

Bedenke mal den Fall, wo der Getter eine Funktion ist, die den Record dynamisch zusammensetzt. Wo sollten dann deine Änderungen landen?

dummzeuch 30. Mär 2021 09:44

AW: Record Property verschachtelt - Schreibzugriff
 
Solange die Properties direkt auf die Felder zugreifen, hatte ich eigentlich gedacht, dass es funktioniert. Das mag allerdings bei verschiedenen Delphi-Versionen unterschiedlich sein.

ABER: Sobald Getter und Setter Methoden involviert sind, geht das spätestens zur Laufzeit schief, auch wenn es compiliert. Der Gettter liefert immer eine Kopie des Records zurück. Du änderst dann die Properties der Kopie und das Original bleibt unverändert.

Dasselbe gilt auch beim Aufruf von Methoden, die den Record verändern: Sie ändern die Kopie.

Edit: Uwe war schneller.

himitsu 30. Mär 2021 10:26

AW: Record Property verschachtelt - Schreibzugriff
 
Nein, es gibt immer eine Kopie und selbst wenn nicht, dann wäre es ein gefährlich/zufälliges Verhalten .. mit Getter oder nicht, mal Kopie oder nicht.

Man kann hier mit Klassen arbeiten
oder mit Properties im Record *1

1) im Record eine Referenz auf das Original und im Setter des Record-Property dann nicht nur im Record, sondern auch im Original ändern.




Toll wäre es, wenn Emba mal auf seine kunden hören und nicht zuviele Feature-/Bugreports sinnlos schließen würde.
Bei einem Schreibzugriff auf ein Property muß am Ende einfach nur nochmal der Setter aufgerufen werden.
Delphi-Quellcode:
X.MeinProperty.xxx := irgendwas

// wird ja als kompiliert
Temp := X.GetMeinProperty;
Temp.xxx := irgendwas
// und am Ende fehlt einfach nur noch eine implizite Zeile, nach dem Schreibzugriff
X.SetMeinProperty(Temp);

Stevie 30. Mär 2021 10:32

AW: Record Property verschachtelt - Schreibzugriff
 
Geht auch mehrfach verschachtelt:
Delphi-Quellcode:
  TTestObj1 = class( TObject)
  private
    FTest2: TTestRec2;
  public
    property IntValue: Integer read FTest2.Test1.IntValue write FTest2.Test1.IntValue;
  end;

...

var
  ltest: TTestObj1;
begin
  ltest := TTestObj1.Create;
  ltest.IntValue := 10;
end;
:cool:

Zum Problem an sich haben die anderen ja schon was gesagt - der Kern ist hier die API - die definiert hier eine Eigenschaft eines Wertetypens, und auch wenn der Zugriff auf ein Feld ohne Getter durch den Compiler letztlich im Binärcode als direkter Speicherzugriff implementiert wird und es auch einige Schlupflöcher(*) gibt, wird hier sichergestellt, dass man durch das direkte Zuweisen des Feldes auf das, was eine Kopie sein könnte nicht passiert.

(*)
1. Durch das Hinzufügen von Methoden zu Recordtypen wurde das ganze ein bisschen kompliziert gemacht - Beispiel:

Delphi-Quellcode:
type
  TTestRec = record
  public
    IntValue: Integer;
    procedure SetValue(i: Integer);
  end;

  TTestObj = class
  private
    FTest: TTestRec;
    function GetTest2: TTestRec;
  public
    property Test: TTestRec read FTest;
    property Test2: TTestRec read GetTest2;
  end;

procedure TTestRec.SetValue(i: Integer);
begin
  IntValue := i;
end;

function TTestObj.GetTest2: TTestRec;
begin
  Result := FTest;
end;

procedure Test;
var
  t: TTestObj;
begin
  t := TTestObj.Create;
  t.Test.SetValue(10);
  t.Test2.SetValue(20);
  Writeln(t.Test.IntValue); // was kommt raus? Spoiler: 10
end;
2. Wenn Eigenschaften direkt auf ein Feld gehen, kann man mit @ auf die Addresse "durchgreifen":

Delphi-Quellcode:
procedure SetValue(val: PInteger; i: Integer);
begin
  val^ := i;
end;

...

procedure Test;
var
  t: TTestObj;
begin
  t := TTestObj.Create;
  SetValue(@t.Test.IntValue, 30); // funktioniert
  SetValue(@t.Test2.IntValue, 40); // funktioniert nicht - compilefehler E2036 Variable required
end;

joachimd 30. Mär 2021 10:57

AW: Record Property verschachtelt - Schreibzugriff
 
und als Ergänzung, wenn Du mit Klassen statt Records arbeitest: Verwende Interfaces, damit Du Dich nicht mehr um das Release kümmern musst (zB beim Zuweisen eines neuen Objekts an das Property).

lxo 30. Mär 2021 13:29

AW: Record Property verschachtelt - Schreibzugriff
 
:thumb: Vielen Dank für die ausführlichen Erklärungen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:34 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