AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign TObjectList sauber an function übergeben und wieder zurück

TObjectList sauber an function übergeben und wieder zurück

Ein Thema von norwegen60 · begonnen am 9. Jul 2020 · letzter Beitrag vom 11. Jul 2020
Antwort Antwort
Seite 1 von 2  1 2   
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
504 Beiträge
 
Delphi 12 Athens
 
#1

TObjectList sauber an function übergeben und wieder zurück

  Alt 9. Jul 2020, 23:23
Hallo.

ich habe eine TObjectList, bei der ich verschiedene Berechnungen vornehmen will. Ich suche den sauberen Weg, wie ich die Liste an die Funktionen übergeben und des Ergebnis wieder zurück gebe. Dabei soll es möglich sein, dass die SourceList unverändert bleibt.

Hier ein abgespecktes Beispiel
Delphi-Quellcode:
type
  TTest = class
  private
    FValue: Double;
  public
    property Value: Double read FValue write FValue;

    constructor Create;
    destructor Destroy; override;
  end;

  TTestList = class(TObjectList<TTest>);

implementation

procedure PrePareList(SourceList, DestList: TTestList);
// Erstellt Liste und kopiert Werte von SourceList dort hin
var
  i: Integer;
  lTest: TTest;

begin
  if DestList <> nil then
    DestList.Free;
  DestList := TTestList.Create(true);

  for i := 0 to SourceList.Count - 1 do
  begin
    lTest := TTest.Create;
    DestList.Add(lTest);
    lTest.Value := SourceList[i].Value;
  end;
end;

function DoAdding(SourceList, DestList: TTestList):Boolean;
var
  i: Integer;
  lTempList: TTestList;

begin
  PrePareList(SourceList, DestList: TTestList); // Damit wird in jedem Fall DestList = SourceList zurück gegeben

  for i := 0 to DestList.Count - 1 do
    DestList[i].Value := DestList[i].Value + 5;

  Result := true; // Wenn korrekt ausgeführt. In Beispiel einfach immer
end;

function DoMultiplicate(SourceList, DestList: TTestList):Boolean;
var
  i: Integer;
  lTempList: TTestList;

begin
  PrePareList(SourceList, DestList: TTestList); // Damit wird in jedem Fall DestList = SourceList zurück gegeben

  for i := 0 to DestList.Count - 1 do
    DestList[i].Value := DestList[i].Value * 5;

  Result := true; // Wenn korrekt ausgeführt. In Beispiel einfach immer
end;

procedure DoCalculations(SourceList, DestList: TTestList);
var
  lTempList: TTestList;

begin
  PrePareList(SourceList, DestList: TTestList); // Damit wird in jedem Fall DestList = SourceList zurück gegeben

  if FlagAddieren then
  begin
    if DoAdding(DestList, lTempList: TTestList) then
    begin
      DestList.Free;
      DestList := lTempList;
      lTempList := nil;
    end;
  end;

  if FlagMultiplicate then
  begin
    if DoMultiplicate(DestList, lTempList: TTestList) then
    begin
      DestList.Free;
      DestList := lTempList;
      lTempList := nil;
    end;
    // Bei false wären Daten ja immer noch unverändert in DestList. Abder ein mögliches Speicherleck
  end;
end;

procedure Start;
var
  i: Integer;
  lStartList, lResultList: TTestList;
  lTest: TTest;

begin
  lStartList := TTestList.Create(true);
  try
    for i := 1 to 5 do
    begin
      lTest := TTest.Create;
      lStartList.Add(lTest);
      lTest.Value := i;
    end;

    DoCalculations(lStartList, lResultList: TTestList);

  finally
    lStartList.Free;
    lResultList.Free;
  end;
end;
So das Prinzip.

Jetzt aber die Fragen:
  • Wie mache ich das sauber, so dass der Speicher sauber created und wieder gefreet wird
  • Der Rechenablauf minimiert wird
  • Wann ist bei der Übergabe an die procdure/function var nötig/sinnvoll procedure DoCalculations(var SourceList, DestList: TTestList);
  • Welche Vorteile böten sich bei lTempList := DoAdding(DestList) . Wo findet hier sinnvollerweise das lTempList := TTestList.Create statt. In der function oder vor dem Aufruf
  Mit Zitat antworten Zitat
Benutzerbild von scrat1979
scrat1979

Registriert seit: 12. Jan 2007
Ort: Sulzbach a.d. Murr
1.026 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 00:01
Antwort zu Punkt 3: den „var“-Parameter kannst du dir sparen, Objekte werden immer über Call-By-Reference (also quasi var) übergeben.
Michael Kübler
  Mit Zitat antworten Zitat
samso

Registriert seit: 29. Mär 2009
439 Beiträge
 
#3

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 07:07
Antwort zu Punkt 3: den „var“-Parameter kannst du dir sparen, Objekte werden immer über Call-By-Reference (also quasi var) übergeben.
Das ist so pauschal nicht richtig. Wenn die Instanz innerhalb der Prozedur erstellt wird, muss sehr wohl mit var bzw. out gearbeitet werden.

Beispiel:

Delphi-Quellcode:
procedure PrePareList(SourceList, DestList: TTestList);
// Erstellt Liste und kopiert Werte von SourceList dort hin
var
  i: Integer;
  lTest: TTest;

begin
  DestList.Free;
  DestList := TTestList.Create(true);

  for i := 0 to SourceList.Count - 1 do
  begin
    lTest := TTest.Create;
    DestList.Add(lTest);
    lTest.Value := SourceList[i].Value;
  end;
end;
Diese Konstruktion produziert ein Memoryleak, weil DestList durch den Aufrufer nicht freigegeben werden kann. Denn die Referenz der neu erzeugten Liste wird so nicht an den Aufrufer zurückgegeben. Richtig ist folgende Konstruktion:

Delphi-Quellcode:
procedure PrePareList(SourceList: TTestList; var DestList: TTestList);
// Erstellt Liste und kopiert Werte von SourceList dort hin
var
  i: Integer;
  lTest: TTest;
begin
  DestList.Free;
  DestList := TTestList.Create(true); // <- diese Referenz muss zurück gegeben werden

  for i := 0 to SourceList.Count - 1 do
  begin
    lTest := TTest.Create;
    DestList.Add(lTest);
    lTest.Value := SourceList[i].Value;
  end;
end;
So muss das dann komplett durchgezogen werden. Also:
Delphi-Quellcode:
function DoAdding(SourceList: TTestList; var DestList: TTestList): Boolean;
function DoMultiplicate(SourceList: TTestList; var DestList: TTestList):Boolean;

Geändert von samso (10. Jul 2020 um 07:13 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.534 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 07:10
Das stimmt so nicht. Das Objekt (genauer: die Instanz) selbst wird ohne Angabe von const, var oder out als Call by Value übergeben. Wenn man ein Objekt als Konstantenparameter übergibt, kann man ihm keine andere Instanz zuweisen, aber seine Eigenschaften ändern. Ich würde aber in Routinen ohne triftigen Grund keine Instanzen erzeugen, da man sonst sehr schnell durcheinanderkommt und sich entweder Speicherlecks oder wilde Zeiger fabriziert. Stattdessen bevorzuge ich persönlich folgende Vorgehensweise:
Delphi-Quellcode:
procedure TuWas(const Src, Dest: TSomeList);
var
  i: integer;
begin
  Assert(Assigned(Src), 'Keine Quellliste übergeben');
  Assert(Assigned(Dest), 'Keine Zielliste übergeben');
  Dest.Clear;
  for i := 0 to Src.Count - 1 do
    begin
      // Nur als Beispiel
      Dest.Add(Src[i]);
    end;
end;
[edit] Mist, zu langsam [/edit]
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
504 Beiträge
 
Delphi 12 Athens
 
#5

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 08:33
Zunächst Danke für die schnellen Antworten
Das stimmt so nicht. Das Objekt (genauer: die Instanz) selbst wird ohne Angabe von const, var oder out als Call by Value übergeben.
Dass Objekte kein var brauchen dachte ich anfangs auch habe aber eben festgestellt, dass es unterschiedlich reagiert.
Um es sicher zu stellen:
  • Wenn das Objekt in der Funktion erzeugt wird, muss ein var dazu
  • Wenn man verhindern will, dass eine neue Instanz erzeugt wird (Create), kann man das mit const verhindern

Ich würde aber in Routinen ohne triftigen Grund keine Instanzen erzeugen, da man sonst sehr schnell durcheinanderkommt und sich entweder Speicherlecks oder wilde Zeiger fabriziert.
Dem stimme ich zu. Grund dafür hier mal anzufragen. Um Sicherheit zu gewinnen

[edit] Mist, zu langsam [/edit]
Das verunsichert mich etwas. Deine Vorredner haben doch was anderes gezeigt. Nämlich wie die Instanzen in der Routine erzeugt werden.

Diese Konstruktion produziert ein Memoryleak, weil DestList durch den Aufrufer nicht freigegeben werden kann. Denn die Referenz der neu erzeugten Liste wird so nicht an den Aufrufer zurückgegeben.
Das hatte ich festgestellt. Jetzt weiß ich warum.

Letzte Frage:
Könnte Aufbau lTempList := DoAdding(DestList) Vorteile bieten? Was muss da beachtet werden bzw. stimmt das hier?
Delphi-Quellcode:
function PrePareList(SourceList: TTestList): TTestList;
// ------------------------------------------------------------------------------------------
// Erstellt Liste und kopiert Werte von SourceList dort hin
var
  i: Integer;
  lTest: TTest;
begin
  Result.Clear;

  for i := 0 to SourceList.Count - 1 do
  begin
    lTest := TTest.Create;
    Result.Add(lTest);
    lTest.Value := SourceList[i].Value;
  end;
end;

function DoAddition(SourceList: TTestList): TTestList;
// ------------------------------------------------------------------------------------------
// Führe rechenfunktion durch, die auch auf vorige Werte in der Liste zurückgreift.
// Wenn SourceList und Result auf selbem Objekt liegen, wurden die vorigen Werte durch den Loop schon verändert => Zuerst zusätzliche Instanz schaffen. Oder gibt es andere Möglichkeit?
var
  i: Integer;
  lTest: TTest;
  lTempList:TTestList;
begin
  lTempList := lTempList.Create(true);
  lTempList := PrePareList(SourceList);


  for i := 0 to SourceList.Count - 1 do
  begin
    if i>0 then
      Result.Value := lTempList[i].Value + lTempList[i-1].Value
    else
      Result.Value := lTempList[i].Value;
  end;

  lTempList.Free;
end;

Procedure Start;
// ------------------------------------------------------------------------------------------
// Vorbereitung der Liste und Aufruf der Funktionen
var
  lStartList, lResultList:TTestList;
begin
  lStartList := TTestList.Create(true);
  lResultList := TTestList.Create(true);

  ...

  lResultList := PrePareList(SourceList);
  
  // Und hier mein Problem. Da die Berechnungen aufeinander aufbauen sollen, ist Result-Objekt = Übergabe-Objekt. Wenn jeder Eintrag für sich geändert wird kein Problem.
  // Wenn die Funktion aber auf vorige Einträge zugreift, wären die schon verändert. Jetzt müsste ich in der Funktion zuerst eine Kopie erzeugen. Korrekt?

  lResultList := DoAddition(lResultList);

  ...

  lStartList.Free;
  lResultList := TTestList.Create(true);
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.534 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 08:52
Wo soll das Funktionsergebnis (also die Instanz) denn herkommen, wenn sie nicht innerhalb der Funktion erzeugt wird? Was spricht denn dagegen, beide Instanzen in der aufrufenden Routine zu erzeugen und als Konstanten-Parameter zu übergeben?
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
504 Beiträge
 
Delphi 12 Athens
 
#7

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 09:20
Wo soll das Funktionsergebnis (also die Instanz) denn herkommen, wenn sie nicht innerhalb der Funktion erzeugt wird? Was spricht denn dagegen, beide Instanzen in der aufrufenden Routine zu erzeugen und als Konstanten-Parameter zu übergeben?
Es spricht nichts dagegen. Es geht mir ums Verständnis. Und da frage ich mich, wie das Konstrukt aussehen müsste, wenn man die Liste als function zurück gibt.

Doch ein kleines Problem habe ich doch. Wie muss das dann aussehen?
Delphi-Quellcode:
procedure TuAlles(const SourList, DestList:TTestList);
begin
  TuWas1(SourceList, DestList);
  // Wie gehe ich vor, dass mit dem nächsten Aufruf die vorige DestList als SourceList übergeben wird
  TuWas2(SourceList, DestList);
end;

// Das hier funktioniert nicht mehr, da ja als const hereingegeben und damit die Zuweisung DestList := lTempList; fehl schlägt

procedure TuAlles(const SourList, DestList:TTestList);
var
  lTempList:TTestList;
begin
  PrepareDestList(SourceList, DestList);

  lTempList:=TTestList.Create;
  PrepareDestList(SourceList, lTempList);

  if TuWas1(DestList, lTempList)then
  begin
    DestList.Free; // Jetzt muss ich die ja freigeben um danach meine neue Ergebnisse dran zu hängen
    DestList := lTempList;
    lTempList:= nil;
  end;
    
  lTempList:=TTestList.Create;
  PrepareDestList(SourceList, lTempList);

  if TuWas2(DestList, lTempList)then
  begin
    DestList.Free;
    DestList := lTempList;
    lTempList:= nil;
  end;
end;

Geändert von norwegen60 (10. Jul 2020 um 09:37 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.534 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 09:53
Ich würde ohne Grund nicht an den Instanzen herumspielen, sondern nur an den enthaltenen Daten.
Delphi-Quellcode:
procedure Tuwas(sonst Src, Dest: TSomeList);
begin
  // Mit den Daten der Listen arbeiten
end;

procedure TuAlles;
var
  List1: TSomeList;
  List2: TSomeList;
  List3: TSomeList;
begin
  List3 := nil;
  List2 := nil;
  List1 := TSomeList.Create;
  try
    List2 := TSomeList.Create;
    TuWas(List1, List2);
    List3 := TSomeList.Create;
    TuWas(List2, List3);
  finally
    List1.Free;
    List2.Free;
    List3.Free;
  end;
end;
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
504 Beiträge
 
Delphi 12 Athens
 
#9

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 10:17
In der Art hatte ich es ursprünglich. Da die Berechnungen zum Teil aufwändig sind, führe ich sie nur noch aus, wenn sie auch benötigt wird
Delphi-Quellcode:
  if FlagTuWas1 then
    TuWas1(List1, List2);

  if FlagTuWas2 then
    TuWas2(List2, List3);
was so dann natürlich nicht geht. Wenn TuWas1 nicht ausgeführt würde müsste ich List1 statt List2 in TuWas2 eingeben

Und der Ansatz
Delphi-Quellcode:
var
  lTempList:TTempList;
begin
  PrepareDestList(SourceList, DestList);

  lTempList:= TAbsBaseList.Create(true);
  try
    // -------------------------------------------------------------------------------------------------------------------------------------
    if FlagTuWas1 then
      if TuWas1(DestList, lTempList) then // Wenn erfolgreich ausgeführt
        PrepareDestList(lTempList, DestList); // Ergebnis in Weitergabe-/Rückgabeliste kopieren

    // -------------------------------------------------------------------------------------------------------------------------------------
    if FlagTuWas2 then
      if TuWas2(DestList, lTempList) then
        PrepareDestList(lTempList, DestList); // Ergebnis in Weitergabe-/Rückgabeliste kopieren
  finally
    lTempList.Free;
  end;
end;
erscheint mir aufwändig, da das neue Ergbnis immer wieder in lTempList kopiert wird um es dann wieder dem nächsten Aufruf übergeben zu können

Geändert von norwegen60 (10. Jul 2020 um 10:42 Uhr) Grund: Hatte die Lsiten vertauscht
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.534 Beiträge
 
Delphi 11 Alexandria
 
#10

AW: TObjectList sauber an function übergeben und wieder zurück

  Alt 10. Jul 2020, 11:47
Benötigst Du denn wirklich mehrere Instanzen? Was genau hast Du eigentlich vor?
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:33 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