Auf record elemente schreiben, wenn nur records im Setter sind ..

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1617
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Auf record elemente schreiben, wenn nur records im Setter sind ..

Beitrag von corpsman »

Servus,

Ich habe folgende Klasse definiert:

Code: Alles auswählen

Type

  TRecord = Record
    x, y: integer;
  End;

  { TClass }

  TClass = Class
  private
    FElements: Array Of TRecord;
    Function getElement(Index: integer): TRecord;
    Procedure SetElement(Index: integer; AValue: TRecord);
  public
    Property Element[Index: integer]: TRecord read getElement write SetElement; default;

    Constructor Create();
    Function GetAddressOfElement(index: integer): Pointer;
  End; 
Nun möchte ich auf x oder y schreiben (alles unter der Annahme das alles drumrum sauber initialisiert wurde:

Code: Alles auswählen

Procedure TForm1.Button1Click(Sender: TObject);
Var
  elem: TRecord;
Begin
  elem.x := 1;
  elem.y := 2;
  fClass[0] := elem;
  fClass[1].x := 2; // -- Das hier geht nicht
End; 
Wie oben im Kommentar zu sehen ist, geht das nicht, wenn ich aber stattdessen den "Umweg" mache geht es, nur sieht es nicht mehr wirklich schön aus:

Code: Alles auswählen

Procedure TForm1.Button1Click(Sender: TObject);
Begin
  TRecord(fClass.GetAddressOfElement(1)^).x := 2;
End; 
Hab euch das ganze Projekt auch mal als Anlage ran gemacht.

Die Frage ist nun geht das auch Hübscher ?
Randbedingung, in meiner Echten implementierung ist Record ein Generischer Typ, d.h. TClass kennt dessen Elemente nicht mal, ..
Dateianhänge
Example.zip
(2.11 KiB) 34-mal heruntergeladen
--
Just try it

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: Auf record elemente schreiben, wenn nur records im Setter sind ..

Beitrag von theo »

Ich weiss nicht ganz genau, worauf du hinaus willst, aber normalerweise gibt es dafür eine Funktion.
Es gibt ja z.B. in der Unit "Classes" den Typ "TPoint" und die dazugehörige Fkt. "Point" ist auch vorhanden.

Code: Alles auswählen

FElements: Array Of TPoint;
...
FElements[0]:=Point(1,2);    
Meinst du das?

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1617
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Auf record elemente schreiben, wenn nur records im Setter sind ..

Beitrag von corpsman »

ich will darauf hinaus, dass ich eine Klasse habe welche ein Array of record hat (hier ein "Tpoint" zur Vereinfachung). Die Klasse stellt nun den Zugriff auf alle Elemente des Arrays zur Verfügung. Das geht auch und ist gut.

Lesen kann man ganz einfach über den Punkt operator, auf obiges Beispiel bezogen ist das hier kein Problem:

Code: Alles auswählen

Procedure TForm1.Button1Click(Sender: TObject);
Var
  i:integer;
Begin
  i := fClass[1].x;
End; 
Drehe ich es aber um, will also auf ein Element des Records schreiben, geht dies nicht, oder nur mittels:

Code: Alles auswählen

Procedure TForm1.Button1Click(Sender: TObject);
Var
  elem:Trecord;
Begin
  elem := fClass[1];
  elem.x := 2;
  fClass[1] := elem;
End; 
Hier wird also das gesamte Element fClass[1] erst in Elem kopiert um nach der Zuweisung von x wieder zurück kopiert zu werden.

Code: Alles auswählen

Procedure TForm1.Button1Click(Sender: TObject);
Begin
  TRecord(fClass.GetAddressOfElement(1)^).x := 2;
End; 
Zumindest in meiner Vorstellung müsste das hier schneller sein, da hier nichts kopiert wird.

Bei einem Beispiel wir "TPoint" ist das sicherlich fraglich, aber in meiner Anwendung habe ich deutlich "Breitere" records ...
--
Just try it

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: Auf record elemente schreiben, wenn nur records im Setter sind ..

Beitrag von theo »

Viele Wege führen nach Rom. :wink:
Das müsste eigentlich auch so klappen:

Code: Alles auswählen

{$modeswitch ADVANCEDRECORDS}
..
type

  TRecord = record
  private
    fX:Integer;
    fY:Integer;
  public
    property X: integer read fX write fX;
    property Y: integer read fY write fY;
  end;    
...
 FElements: Array Of TRecord;       
..
 SetLength(FElements,2);
 FElements[0].X:=23;
 FElements[1].Y:=24;
 Caption:=FElements[1].Y.ToString;    
  

Warf
Beiträge: 2118
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Auf record elemente schreiben, wenn nur records im Setter sind ..

Beitrag von Warf »

Das Problem ist das der Access auf den Member nicht den Setter aufruft sondern den Getter, du Holst dir also eine Temporäre Kopie des Records durch den Getter und Greifst auf dessen X zu. Selbst wenn der Compiler das nicht abfangen würde, würde es nicht den Korrekten Record ändern, sondern nur die Temporäre Kopie die du aus dem Getter bekommst.

Die einfachste lösung ist ne simple update funktion:

Code: Alles auswählen

function UpdateX(const InpRec: TMyRec; NewX: Integer): TMyRec;
begin
  Result := InpRec;
  Result.X := NewX;
end;

  MyClass.Records[i] := UpdateX(MyClass.Records[i], 42);
Mit advanced Records geht das auch als Methode oder als Operator:

Code: Alles auswählen

Type TMyRecord = record
  X: Integer;
  function UpdateX(NewX: Integer);
end;

function TMyRecord.UpdateX(NewX: Integer): TMyRec;
begin
  Result := Self;
  Result.X := NewX;
end;

  MyClass.Records[i] := MyClass.Records[i].UpdateX(42);
Das ist grundsätzlich die sauberste Lösung weil du nicht im Speicher der von einem Anderen Objekt Gemanaged wirst rumhantierst. Man hat hier eine All or Nothing Eigenschaft, das man nur Records als ganze Updaten kann (und somit z.B. Plausibilitätschecks zu jedem Zeitpunkt durchführen kann). Macht auch Synchronisation für multithreaded Anwendungen massiv viel einfacher

Alternativ wenn du inplace arbeiten willst kannst du auch einfach dir einen Pointer auf den Record rausgeben lassen:

Code: Alles auswählen

PMyRec = ^TMyRec;

TMyClass = class
  ...
  property ItemPtr[AIndex: Integer]: PMyRec read GetItemPtr;
end;

MyInstance.ItemPtr[i]^.X := 42;
Hier musst du aber sicher sein das der Pointer Zugriff alles richtig macht, weil du keinen Setter und Damit keine Plausibilitätschecks zwischenschalten kannst. Außerdem würden potentielle Events die der Setter Triggert nicht ausgeführt und müssten Manuell Nachgecalled werden. Von daher ist das die Unsauberere Lösung, ist aber deutlich Effizienter (Laufzeit und Speicher, da keine Zwischenkopien erstellt werden).

Daher würde ich meist empfehlen eine Updater Funktion zu schreiben, und nur wenn du Performance Probleme hast auf die Inplace Variante wechseln

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1617
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Auf record elemente schreiben, wenn nur records im Setter sind ..

Beitrag von corpsman »

*g*

danke für die Antworten. Auf die Pointer Variante von Warf bin ich ja alleine gekommen nur eben nicht so gut erklärt.
Das UpdateX ist dann die elegante aber Speicher ineffiziente Version wie befürchtet.
Danke fürs beleuchten.
--
Just try it

Antworten