![]() |
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:
Der Aufruf z.B. soll so aussehen:
function GetTiere: TStringList;
begin Result:=TStringList.create; Result.Add('Hund'); Result.Add('Katze'); Result.Add('Maus'); end;
Delphi-Quellcode:
Also ich will per Assign aus einer Funktion eine Listbox füllen.
Listbox1.Items.Assign(GetTiere);
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. |
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 |
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. |
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:
Dann kann man sie so aufrufen:
Procedure FillTiere (aDestination : TStringList);
begin aDestination.Add('Hund'); aDestination.Add('Katze'); aDestination.Add('Maus'); end;
Delphi-Quellcode:
oder so:
FillTiere(ListBox.Items)
Delphi-Quellcode:
Somit ist klar, wer Ersteller ist.
tmp := TStringList.Create;
Try FillTiere (tmp); ListBox.Items.Assign(tmp); finally tmp.Free end |
AW: TStringList als Result einer Funktion
Was spricht dagegen das als:
Delphi-Quellcode:
zu realiseren
GetTiere(Listbox1.Items)
Zitat:
|
AW: TStringList als Result einer Funktion
Zitat:
|
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! |
AW: TStringList als Result einer Funktion
Zitat:
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. |
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:
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. |
AW: TStringList als Result einer Funktion
Zitat:
(Warum nimmt der auch nicht das, was ich meinem Profil eingestellt habe) Habs korrigiert. |
AW: TStringList als Result einer Funktion
Zitat:
|
AW: TStringList als Result einer Funktion
Werde in Zukunft auf Dein Profil achten :)
|
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:
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)
procedure Assign(Value: TStringList);
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. |
AW: TStringList als Result einer Funktion
Zitat:
Zitat:
|
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; |
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); |
AW: TStringList als Result einer Funktion
Zitat:
Delphi-Quellcode:
die übergebene Liste zu löschen oder aber die Daten unten oder sonstwo anzuhängen. Miteiner Funktion bist Du nicht so flexibel.
Dest.Clear;
Gruß K-H P.S. Irgendwann vergisst jeder einmal die Liste wieder frei zu geben. |
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.
|
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; |
AW: TStringList als Result einer Funktion
Na ja. Formal korrekt. Würdest Du das so schreiben und verwenden?
|
AW: TStringList als Result einer Funktion
Das find ich aber sehr verwirrend, denn ob man nun schreibt
Delphi-Quellcode:
oder
sl := CreateList(TStringList.Create);
Delphi-Quellcode:
, tut sich im Endergebnis nichts.
sl := CreateList(nil);
|
AW: TStringList als Result einer Funktion
Zitat:
Delphi-Quellcode:
Oder man erkennt aus dem Funktionsnamen direkt, daß eine Klasse erzeugt wird.
sl:=TStringlist.create;
FillTiere(sl);
Delphi-Quellcode:
function StringlistCreateWithTiere: TStringList;
begin Result:=TStringList.create; Result.Add('Hund'); Result.Add('Katze'); Result.Add('Maus'); end; |
AW: TStringList als Result einer Funktion
Zitat:
|
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:
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.
function ReturnsStrings: TStrings;
begin Result := TStringlist.Create; ... end; for i := 0 to ReturnsStrings.Count - 1 do Bearbeite(ReturnsStrings[i]); |
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: |
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:
wird weggelassen und der Name beginnt mit Appendxxx.
Clear
Delphi-Quellcode:
Je nach Anwendungsfall kann man das
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;
Delphi-Quellcode:
auch dazunehmen; nur dann beginnt die Prozedure mit Fillxxx.
Clear
Wenn ich weiss, dass die Prozedure nicht mehr als 100 Einträge erzeugen wird, dann lasse ich auch
Delphi-Quellcode:
und
BeginUpdate
Delphi-Quellcode:
weg.
EndUpdate
|
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; |
AW: TStringList als Result einer Funktion
Zitat:
Ne :tongue: Append wird ja immer gemacht, aber das Clear wird gesteuert.
Delphi-Quellcode:
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".
procedure FillWithAnimals(Dest: TStrings; Clear: Boolean=False);
// oder doch AppendAnimals (ohne Denglisch), ist nun auch egal, wobei es ja dennoch immer Append macht 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 |
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:
mit default
Append
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:
heissen.
procedure AppendAnimals( Dest: TStrings; Clear: Boolean=False );
|
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.
|
AW: TStringList als Result einer Funktion
Oder man schreibt einfach für jeden Anwendungsfall eine Methode.
Delphi-Quellcode:
Wobei man die beiden ersten Methoden durch die dritte abbilden kann.
Type
ITiereUndListenDingenskirchen = interface Procedure InsertAtFront (aDestination : TStrings); Procedure InsertAtEnd (aDestination : TStrings); Procedure InsertAt (aDestination : TStrings; aIndex : Integer); Procedure Replace (aDestination : TStrings); ... 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:
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:).
Type
ITiereUndListenDingenskirchen = interface Procedure InsertAt (aDestination : TStrings; aIndex : Integer); ... |
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 |
AW: TStringList als Result einer Funktion
Zitat:
|
AW: TStringList als Result einer Funktion
Zitat:
|
AW: TStringList als Result einer Funktion
Oder: Zu viele Entwickler enterben den Code.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:31 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz