Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   TStringList als Result einer Funktion (https://www.delphipraxis.net/181498-tstringlist-als-result-einer-funktion.html)

Captnemo 20. Aug 2014 09:23

Delphi-Version: XE4

TStringList als Result einer Funktion
 
Hi,

ist ne blöde Frage, ich weiß, aber ich stell sie mal trotzdem.
Ich brauch eine Funktion, die mir eine StringList zurückliefert.

Delphi-Quellcode:
function GetTiere: TStringList;
begin
  Result:=TStringList.create;
  Result.Add('Hund');
  Result.Add('Katze');
  Result.Add('Maus');
end;
Der Aufruf z.B. soll so aussehen:

Delphi-Quellcode:
Listbox1.Items.Assign(GetTiere);
Also ich will per Assign aus einer Funktion eine Listbox füllen.

Wo würde man aber Result wieder freigeben? Theoretisch kann die Funktion GetTiere ja mehrmals aufgerufen werden, und jedes Mal würde eine neue StringList erstellt, die dann im Speicher rumliegt.
In der Funktion selber freigeben würde ja logischerweise nicht gehen.

Sherlock 20. Aug 2014 09:29

AW: TStringList als Result einer Funktion
 
Meine naive Meinung: Wers erzeugt gibt auch wieder frei. In diesem Fall ist der Aufrufer der Funktion der Erzeuger.

Sherlock

TheFrog 20. Aug 2014 09:34

AW: TStringList als Result einer Funktion
 
Wir haben in unserem Projekt ein Interface IMyStringList deklariert. Die implementierende Klasse ist von TInterfacedObject abgeleitet und dient als Wrapper für eine TStringList.

Damit könnte dein Aufruf ungefähr so aussehen:

Delphi-Quellcode:
ListBox1.Items.Assign(GetTiere.Strings);


Damit wird durch das ReferenceCounting die Instanz wieder freigegeben, wenn diese nicht mehr gebraucht wird.

Vielleicht hilft Dir das ja weiter.

Dejan Vu 20. Aug 2014 09:36

AW: TStringList als Result einer Funktion
 
Wenn man interfaces benutzt, sollte man nur interfaces nehmen und keinen Mischmasch. Denn wie erkennt man, wo man etwas manuell freigeben muss und wo nicht? Also nichts gegen die Lösung, aber als isolierte Lösung hier würde ich das nicht machen.

Die eigentliche Frage ist ja (wenn man Sherlock's law beachtet) Wie erkennt man (wie im Beispiel des TE), das die Funktion eine Liste liefert, die man freigeben muss? Wer ist hier wirklich der Ersteller?

Ich würde das so lösen, das die Funktion eine Methode ist, die in eine ihr übergebene Liste die Tiernamen anhängt, und eben keine neue Instanz liefert.

Delphi-Quellcode:
Procedure FillTiere (aDestination : TStringList);
begin
  aDestination.Add('Hund');
  aDestination.Add('Katze');
  aDestination.Add('Maus');
end;
Dann kann man sie so aufrufen:
Delphi-Quellcode:
FillTiere(ListBox.Items)
oder so:
Delphi-Quellcode:
  tmp := TStringList.Create;
  Try
    FillTiere (tmp);
    ListBox.Items.Assign(tmp);
  finally
    tmp.Free
  end
Somit ist klar, wer Ersteller ist.

Bernhard Geyer 20. Aug 2014 09:36

AW: TStringList als Result einer Funktion
 
Was spricht dagegen das als:

Delphi-Quellcode:
GetTiere(Listbox1.Items)
zu realiseren

Zitat:

Zitat von TheFrog (Beitrag 1269201)
Wir haben in unserem Projekt ein Interface IMyStringList deklariert.

Wieso nicht die Standardklasse verwendet: docwiki.embarcadero.com/Libraries/XE5/de/System.Classes.IStringsAdapter

Captnemo 20. Aug 2014 09:39

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1269203)
Was spricht dagegen das als:

Delphi-Quellcode:
GetTiere(Listbox1.Items)
zu realiseren

Zitat:

Zitat von TheFrog (Beitrag 1269201)
Wir haben in unserem Projekt ein Interface IMyStringList deklariert.

Wieso nicht die Standardklasse verwendet: docwiki.embarcadero.com/Libraries/XE5/de/System.Classes.IStringsAdapter

Hmmm....absolut nichts spricht dagegen. Aus irgendeinem nicht nachvollziehbaren Grund wollte ich das umgekehrt machen :-) Frag mich nicht warum.

TheFrog 20. Aug 2014 09:41

AW: TStringList als Result einer Funktion
 
Ich dachte er hat als Delphi - Version die 5 angegeben.

Ist damit XE5 gemeint, dann hast Du natürlich recht!

Bernhard Geyer 20. Aug 2014 09:41

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von Captnemo (Beitrag 1269204)
Hmmm....absolut nichts spricht dagegen. Aus irgendeinem nicht nachvollziehbaren Grund wollte ich das umgekehrt machen :-) Frag mich nicht warum.

Zu viel C#/Java programmiert.
Bei einer Programmiersprache mit Garbage-Collector würde ich das so machen.
Da wird ja alles korrekt aufgeräumt wenn es nicht mehr verwendet würde.

himitsu 20. Aug 2014 09:41

AW: TStringList als Result einer Funktion
 
Man sollte aber im Namen/Dokumentation erwähnen, daß ob die Funktion jedesmal eine TStringList erzeugt, zurückgibt und nicht wieder freigibt
oder ob sich die Funktion die Instanzen merkt und "irgendwann" freigibt
oder ob es eine interne Instanz ist, welche mit der Komponente freigegeben wird. (mehrfacher aufruf und gleichzeitige Auswertungen der Liste nicht möglich)
oder ob der Aufrufer für die Freigabe verantwortlich ist.

Zitat:

Zitat von Sherlock (Beitrag 1269200)
Meine naive Meinung: Wers erzeugt gibt auch wieder frei. In diesem Fall ist der Aufrufer der Funktion der Erzeuger.

Erstellt hat die Liste aber die Funktion und nicht der Aufrufer.
Außer man sagt, daß die Funktion eine Liste erstellt und der Aufrufer das dann freigeben soll.

Ich gebe ja auch nicht jedesmal die Liste frei, wenn ich auf Memo.Lines (intern Memo.GetLines) zugreife.


PS: Auch wenn du intern eine TStringList erzeugst ... Es macht sich dennoch oft besser, wenn das Result-Typ ein TStrings ist.

Captnemo 20. Aug 2014 09:45

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von TheFrog (Beitrag 1269205)
Ich dachte er hat als Delphi - Version die 5 angegeben.

Weil ich das blöderweise IMMER vergesse :oops::oops:
(Warum nimmt der auch nicht das, was ich meinem Profil eingestellt habe)

Habs korrigiert.

Captnemo 20. Aug 2014 09:46

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von himitsu (Beitrag 1269207)
PS: Auch wenn du intern eine TStringList erzeugst ... Es macht sich dennoch oft besser, wenn das Result-Typ ein TStrings ist.

Kannst du mir auch sagen warum das sich besser macht?

TheFrog 20. Aug 2014 09:49

AW: TStringList als Result einer Funktion
 
Werde in Zukunft auf Dein Profil achten :)

himitsu 20. Aug 2014 09:59

AW: TStringList als Result einer Funktion
 
Ihr dürft im DP-Projekt gern für meinen Feature-Request voten. Ich hatte mir mal gewünscht, daß entweder die Profilangabe als Default erstmal vorausgewählt ist, oder daß diese Angabe per Vorauswahl leer ist.

Typkompatibilität, vorallem wenn man diese StringListe an eine andere Funktion übergeben will.

Stell dir mal vor jemand hätte das Assign als
Delphi-Quellcode:
procedure Assign(Value: TStringList);
deklariert und schön könnte man nur noch TStringLists und nix Anderes mehr übergeben. Darum am Besten gleich durchgängig den kleinsten gemeinsamen Nenner nutzen. (siehe TMemo.Lines ... da ist z.B. eigentlich ein TMemoStrings drin)
Bei deinem Result kommt es ja nur auf die Textinhalte an und es ist vollkommen egal wo die gespeichert sind und für den Zugriff daraus reichen die Schnittstellen des TStrings auch vollkommen aus.

Captnemo 20. Aug 2014 10:05

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von himitsu (Beitrag 1269212)
Ihr dürft im DP-Projekt gern für meinen Feature-Request voten. Ich hatte mir mal gewünscht, daß entweder die Profilangabe als Default erstmal vorausgewählt ist, oder daß diese Angabe per Vorauswahl leer ist.

:thumb: Mach ich gleich mal :thumb:

Zitat:

Zitat von himitsu (Beitrag 1269212)
Typkompatibilität, vorallem wenn man diese StringListe an eine andere Funktion übergeben will.

Herzlichen Dank.

Perlsau 20. Aug 2014 11:42

AW: TStringList als Result einer Funktion
 
Delphi-Quellcode:
function GetTiere: String;
Var
  Liste: TStrings;
begin
  Liste:=TStringList.create;
  Try
    Liste.Add('Hund');
    Liste.Add('Katze');
    Liste.Add('Maus');
    Result := Liste.Text;
  Finally
    Liste.Free;
  End;
end;

// Aufruf:
Listbox1.Items.Text := GetTiere;

DeddyH 20. Aug 2014 12:09

AW: TStringList als Result einer Funktion
 
Wie schon eingangs erwähnt:
Delphi-Quellcode:
procedure GetTiere(const Dest: TStrings);
begin
  Assert(Assigned(Dest));
  Dest.BeginUpdate;
  try
    Dest.Clear;
    Dest.Add('Hund');
    Dest.Add('Katze');
    Dest.Add('Maus');
  finally
    Dest.EndUpdate;
  end;
end;

GetTiere(ListBox1.Items);

p80286 20. Aug 2014 14:44

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von DeddyH (Beitrag 1269232)
Wie schon eingangs erwähnt:
Delphi-Quellcode:
procedure GetTiere(const Dest: TStrings);

Hält Dir zusätzlich die Möglichkeit offen mit
Delphi-Quellcode:
Dest.Clear;
die übergebene Liste zu löschen oder aber die Daten unten oder sonstwo anzuhängen. Miteiner Funktion bist Du nicht so flexibel.

Gruß
K-H

P.S.
Irgendwann vergisst jeder einmal die Liste wieder frei zu geben.

Dejan Vu 20. Aug 2014 15:33

AW: TStringList als Result einer Funktion
 
Es wurde auch schon 1000,3x gesagt: Funktionen sollten in Delphi eben keine Instanzen erzeugen und liefern, außer es ist auch vom Namen her klar, das da was erzeugt wird. Aber trotzdem würde ich das nie machen. Einfach weil.

mkinzler 20. Aug 2014 15:50

AW: TStringList als Result einer Funktion
 
Kompromiss:

Delphi-Quellcode:
function CreateList( list: TStrings): TStrings;
begin
if not Assigned( list) then
   list := TStringList.Create;
   list.Add( 'a');
   list.Add( 'b');
   list.Add( 'b');
   Result := list;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStrings;
begin
  try
    sl := CreateList( TStringList.Create);
    Memo1.Lines.Assign( sl);
  finally
    sl.Free;
  end;
end;

Dejan Vu 20. Aug 2014 15:56

AW: TStringList als Result einer Funktion
 
Na ja. Formal korrekt. Würdest Du das so schreiben und verwenden?

DeddyH 20. Aug 2014 15:58

AW: TStringList als Result einer Funktion
 
Das find ich aber sehr verwirrend, denn ob man nun schreibt
Delphi-Quellcode:
sl := CreateList(TStringList.Create);
oder
Delphi-Quellcode:
sl := CreateList(nil);
, tut sich im Endergebnis nichts.

bernau 20. Aug 2014 16:20

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von mkinzler (Beitrag 1269267)
Kompromiss:

Grauselig. Man muss m.E. nach sofort erkennen, daß ein Objekt erzeugt wird. Dazu gekört für mich der Klassenname in Verbindung mit dem Create. Danach mit einer Funktion füllen (Wie der Vorschlag von Dejan Vu)

Delphi-Quellcode:
sl:=TStringlist.create;
FillTiere(sl);
Oder man erkennt aus dem Funktionsnamen direkt, daß eine Klasse erzeugt wird.

Delphi-Quellcode:
function StringlistCreateWithTiere: TStringList;
begin
   Result:=TStringList.create;
   Result.Add('Hund');
   Result.Add('Katze');
   Result.Add('Maus');
end;

mkinzler 20. Aug 2014 16:43

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von Dejan Vu (Beitrag 1269268)
Na ja. Formal korrekt. Würdest Du das so schreiben und verwenden?

Nein, aus, unter anderem, den genannten Gründen.

DeddyH 20. Aug 2014 16:54

AW: TStringList als Result einer Funktion
 
Ich bin auch überhaupt kein Freund von Funktionen, die Objektinstanzen zurückgeben, welche innerhalb der Funktion angelegt werden. Ich finde ihn gerade nicht, aber wir hatten hier mal einen Thread, wo ungefähr Folgendes gemacht wurde:
Delphi-Quellcode:
function ReturnsStrings: TStrings;
begin
  Result := TStringlist.Create;
  ...
end;

for i := 0 to ReturnsStrings.Count - 1 do
  Bearbeite(ReturnsStrings[i]);
Klar, mit einer einfachen Zwischenvariablen wäre das Problem vom Tisch, aber hätte man gleich eine Prozedur mit einem TStrings-Parameter daraus gemacht, wäre der obige Code so gar nicht möglich gewesen.

himitsu 20. Aug 2014 17:48

AW: TStringList als Result einer Funktion
 
Wenn ReturnsStrings zu diesem Zeitpunkt immer das Selbe zurück gibt, dann würde der Code zumindestens funktionieren. (bis auf ein paar Speicherlecks und eventuell einer langsameren Verarbeitung, jenachdem wie schnell das ReturnsStrings war)

Unter iOS/Android gibt es diese Speicherlecks nicht. :stupid:

sx2008 20. Aug 2014 22:30

AW: TStringList als Result einer Funktion
 
Ich würde ganz klar den Vorschlag von DeddyH (Beitrag #16) bevorzugen wenn auch mit kleinen Änderungen.
Das
Delphi-Quellcode:
Clear
wird weggelassen und der Name beginnt mit Appendxxx.
Delphi-Quellcode:
procedure AppendTiere(const Dest: TStrings);
begin
  Assert(Assigned(Dest));
  Dest.BeginUpdate;
  try
    Dest.Add('Hund');
    Dest.Add('Katze');
    Dest.Add('Maus');
  finally
    Dest.EndUpdate;
  end;
end;
Je nach Anwendungsfall kann man das
Delphi-Quellcode:
Clear
auch dazunehmen; nur dann beginnt die Prozedure mit Fillxxx.
Wenn ich weiss, dass die Prozedure nicht mehr als 100 Einträge erzeugen wird, dann lasse ich auch
Delphi-Quellcode:
BeginUpdate
und
Delphi-Quellcode:
EndUpdate
weg.

Sir Rufo 20. Aug 2014 23:23

AW: TStringList als Result einer Funktion
 
Nur so aus Spass (dann sind doch alle glücklich)
Delphi-Quellcode:
procedure FillWithAnimals( const Dest: TStrings; Append : Boolean = False );
begin
  Assert(Assigned(Dest));
  Dest.BeginUpdate;
  try
    if not Append then
      Dest.Clear;
    Dest.Add('Hund');
    Dest.Add('Katze');
    Dest.Add('Maus');
  finally
    Dest.EndUpdate;
  end;
end;

himitsu 21. Aug 2014 00:10

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von Sir Rufo (Beitrag 1269294)
Nur so aus Spass (dann sind doch alle glücklich)

Ähhhhhhhhhhhhhhhhhhhhhhhhhhh....

Ne :tongue:


Append wird ja immer gemacht, aber das Clear wird gesteuert.


Delphi-Quellcode:
procedure FillWithAnimals(Dest: TStrings; Clear: Boolean=False);
// oder doch AppendAnimals (ohne Denglisch), ist nun auch egal, wobei es ja dennoch immer Append macht
Ja, Const ist schon richtig, da die Referenz wirklich nicht verändert wird, aber da man was in der Klasse verändert (hinzufügt), finde ich es imho etwas "verwirrend".
Wobei var natückich "logisch" gesehn eindeutiger wäre, aber in Bezug auf den Zeiger ja vollkommen falsch ... dann doch lieber ohne.

Mir würde es ja gefallen, wenn man für Klassen/Zeigertypen dennoch ein IN und OUT angeben könnte, als Info für den Entwickler. (wobei man das ja im HelpInsigt/DokuInsigt machen könnte, wenn man es da einführen täte)

Und hier ist das Problem mit der typisierung besonders groß.
> mit VAR könnte man nur genau diesen Typen, als übergebene Variable verwenden und was drin ist, ist egal
> bei TStringList kann man nur TStringList und Nachfahren reingeben, obwohl intern nur Methoden des TStrings verwendet werden
> mit TStrings wäre auch ein Memo.Lines möglich

Sir Rufo 21. Aug 2014 00:37

AW: TStringList als Result einer Funktion
 
Eventuell ist mein englisch eingerostet, aber bei Fill erwarte ich, dass die Liste gefüllt und eben nicht aufgefüllt wird (wäre dann ja Fillup). Darum der Parameter
Delphi-Quellcode:
Append
mit default
Delphi-Quellcode:
False
.

Es kommt also nicht darauf an, was gemacht wird, sondern was der Name nahelegt und logisch ist.

Deine Funktion müsste also logischerweise
Delphi-Quellcode:
procedure AppendAnimals( Dest: TStrings; Clear: Boolean=False );
heissen.

DeddyH 21. Aug 2014 07:05

AW: TStringList als Result einer Funktion
 
Man kann auch einen eigenen Aufzählungstyp samt Parameter dieses Typs definieren, der besagt, ob vorne oder hinten angehängt oder die Liste vorher gelöscht werden soll. Damit sollten dann wirklich alle zufrieden sein.

Dejan Vu 21. Aug 2014 07:28

AW: TStringList als Result einer Funktion
 
Oder man schreibt einfach für jeden Anwendungsfall eine Methode.
Delphi-Quellcode:
Type
  ITiereUndListenDingenskirchen = interface
    Procedure InsertAtFront (aDestination : TStrings);
    Procedure InsertAtEnd (aDestination : TStrings);
    Procedure InsertAt (aDestination : TStrings; aIndex : Integer);
    Procedure Replace (aDestination : TStrings);
    ...
Wobei man die beiden ersten Methoden durch die dritte abbilden kann.

Um die Verwirrung zu maximieren gibt es auch noch den Ansatz, das eine API nur die Elementaroperationen bereit stellen sollte. Wenn ich also eine Liste löschen, und Tiere an beliebiger Stelle in der Liste einfügen kann, dann würde nach diesem Ansatz die 'Append'-Methode mit dem 'Clear'-Parameter nicht erlaubt sein. Und dann wäre das Interface vielleicht auf eine Methode beschränkt.
Delphi-Quellcode:
Type
  ITiereUndListenDingenskirchen = interface
    Procedure InsertAt (aDestination : TStrings; aIndex : Integer);
...
Zum Namen: Append/Fill past schon. 'Fill up' bezeichnet eher das 'voll machen'. Und voll ist die Liste hinterher ja nicht, sondern nur voller (Äh, wobei 'voller' widerum die Steigerung von 'voll' ist, rein grammatikalisch, :gruebel:).

Jumpy 21. Aug 2014 08:31

AW: TStringList als Result einer Funktion
 
Ich les mich gereade ein bißchgen durch CleanCode und da gibt es ja ein ganzes Kapitel über Namensgebung von Funktionen, wo u.a. gesagt wird das so ein Name immer mal wieder verändert wird, bis er endlich das aussagt, was der Entwickler mit der Funktion bezweckt, bzw. bis es ihm gefällt.

Danke euch kann ich mir nun nachvollziehen was gemeint ist. :-D

Allerdings habt ihr mir auch schön das Sprichwort: "Zu viele Köche verderben den Brei" aufgezeigt. :-D

Dejan Vu 21. Aug 2014 09:56

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von Jumpy (Beitrag 1269320)
Allerdings habt ihr mir auch schön das Sprichwort: "Zu viele Köche verderben den Brei" aufgezeigt. :-D

Richtig heißt es: Zu viele Entwickler verderben den Code.

Jumpy 21. Aug 2014 10:02

AW: TStringList als Result einer Funktion
 
Zitat:

Zitat von Dejan Vu (Beitrag 1269336)
Zitat:

Zitat von Jumpy (Beitrag 1269320)
Allerdings habt ihr mir auch schön das Sprichwort: "Zu viele Köche verderben den Brei" aufgezeigt. :-D

Richtig heißt es: Zu viele Entwickler verderben den Code.

Müsste das nicht im OOP "vererben den Code" sein. :-D

Dejan Vu 21. Aug 2014 10:07

AW: TStringList als Result einer Funktion
 
Oder: Zu viele Entwickler enterben den Code.


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