Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Listen als (Formular-)Felder
Terra23 - Sa 23.02.13 00:52
Moderiert von
Martok: Abgetrennt von hier [http://www.entwickler-ecke.de/viewtopic.php?t=15029], Titel an der ersten Antwort orientiert
Sorry, wenn ich diesen alten Thread nochmal ausgrabe, aber ich wollte für die Frage nicht unbedingt einen neuen Thread eröffnen.
Ist es möglich, eine Stringlist in der FormCreate zu erzeugen und sie erst im FormClose zu beenden?
Aktuell, weil ich noch nicht so bewandert bin, arbeite ich mit Hilfskomponenten, d.h. ich lade die Stringliste in eine ListBox oder ein Memo, weil ich im laufenden Programm noch mit den Inhalten arbeiten will.
Kann ich stattdessen also folgendes tun:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| globale Variable = var List: TStrings;
TForm1.FormCreate(Sender: TObject) begin List := TStringList.Create; end;
TForm1.FormClose(Sender: TObject); begin List.Free; end; |
Oder gibt es da noch andere Lösungen? Hilfskomponenten finde ich persönlich nicht sehr professionell...
WasWeißDennIch - Sa 23.02.13 01:18
Das passt nicht ganz zusammen. Wenn Du die Liste (BTW: besser als privates Feld des Formulars deklarieren, globale Variablen sind böse) im OnCreate erzeugst, ist OnDestroy der richtige Zeitpunkt zum Freigeben.
Terra23 - Sa 23.02.13 01:50
Aber generell wäre sowas möglich ohne dass ich immer diese Listen mit Try-Finally-End bzw. Try-Except-End nutzen muss? Wie deklariere ich die Listen denn als privates Feld?
Delphi-Quelltext
1: 2:
| private List: TStrings; |
Wäre das so korrekt?
Bergmann89 - Sa 23.02.13 03:13
Hey,
so ähnlich sollte das bei dir aussehen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| type TForm1 = class(TForm) private fList: TStringList public end; |
MfG Bergmann.
Tranx - Sa 23.02.13 09:25
Dann kannst Du mit einer Property auf die Liste zugreifen:
Delphi-Quelltext
1: 2: 3:
| public property myList : TStringlist read GetList write SetList; |
Wenn Du dann Strg + Umsch + C drückst werden die Funktion und Prozedur automatisch erzeugt:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| Function TForm.GetList : TStringList; begin Result := fList end;
procedure TForm.SetList(Value : TStringList); begin fList := Value; end; |
Wichtig ist natürlich: Im Constructor Create des Formulars und im Destructor Destroy folgende Zeilen hinzufügen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| constructor TForm.Create(AOwner: TWinControl); begin inherited; fList : TStringList.Create(Self); end;
destructor TForm.Destroy; begin fList.Destroy; inherited; end; |
Der Aufruf der Liste erfolgt dann (z.B.):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| myList.Strings.Clear; myList.Strings.Add(Zeile);
Form.myList.Strings.Clear; Form.myList.Strings.Add(Zeile); |
ANMERKUNG:
Wenn Du eine Stringlist so einbindest, dann darfst Du auf keinen Fall zwei Sachen machen:
in einer Prozedur oder Funktion außerhalb von TForm.Create und TForm.Destroy :
TStringlist.Create und TStringlist.Destroy
denn dann würdest Du eine existierende Liste neu erzeugen, oder eine zerstören. Und am Ende würde der Destruktor dann nichts (NIL) zerstören wollen, und das geht nun mal nicht. Das gibt dann einen Laufzeitfehler und Du kannst (normalerweise) das Programm nicht mehr beenden. Du kannst mit den Inhalten arbeiten, aber nie die Liste neu erzeugen oder zerstören. Bitte beim Einbinden und bei der Programmierung daran denken.
Mathematiker - Sa 23.02.13 10:05
Hallo,
unabhängig von den anderen gegebenen Antworten beschäftigt mich:
WasWeißDennIch hat folgendes geschrieben : |
Das passt nicht ganz zusammen. Wenn Du die Liste ... im OnCreate erzeugst, ist OnDestroy der richtige Zeitpunkt zum Freigeben. |
Was spricht gegen OnClose?
Ich habe bisher immer die Listen in der Close-Methode entfernt und hatte noch keine Probleme. Ich lasse mich aber gern überzeugen.
Beste Grüße
Mathematiker
Tranx - Sa 23.02.13 10:46
Das kann stimmen. Wenn OnClose dann den Destruktor aufruft. Aber dann muss OnShow den Construktor aufrufen. Schaue mal nach.
Aber eines ist bei OnClose klar: Erst das Destroy der Komponente einbauen, wenn wirklich das Programm (Formular) beendet wird, da ja möglicherweise die Close-Prozedur ohne Beenden abgeschlossen wird:
Action != caFree !!! (caNone, caHide, caMinimize)
WasWeißDennIch - Sa 23.02.13 14:23
Die Property würde ich weglassen, wenn die Liste nicht von außen zugänglich sein muss. Außerdem sollte man Objekte niemals(!!!) mit Destroy freigeben, sondern immer mit Free. Grund: Free prüft intern auf nil und ruft erst dann ggf. Destroy auf. Und zur Freigabe im OnDestroy, wenn im OnCreate erzeugt, hat Tranx ja bereits alles gesagt.
[edit]Nachtrag: im Setter der Property würde ich keine einfache Zuweisung machen, sondern mit Assign die Eigenschaften der übergebenen Liste kopieren. Ansonsten schafft man sich ein Speicherleck, da man die selbst erzeugte Instanz mit der übergebenen überschreibt und somit nicht mehr freigeben kann. [/edit]
Tranx - Sa 23.02.13 14:44
Wasweißichdenn hat Recht. das mit dem Setxxx habe ich nicht bedacht. Aber das benötigt man auch eigentlich nicht. Es reicht für die Property, falls man von außen auf die Liste zugegriffen werden muss:
Delphi-Quelltext
1:
| property myListe : TStringlist read GetListe; |
dann fällt Setliste weg und man kann die Liste nicht zuordnen. Aber das geschieht eh im Konstruktor.
Klar benötigt man die Property nur, falls man von außen auf die Liste zugreifen muss. Aber wenn ich Eigenschaften kapseln will, dann ist es besser, sie nur der Klasse sichtbar zu machen. Und das ist dann in dem private-Bereich der Klasse. Dann sieht man die Eigenschaft nicht mehr außerhalb der Klasse. Benötigt man die Liste aber gerade da, und das ist bei Listen ja zu erwarten, dann benötigt man die property, die dann über Umwege anderen Klassen diese Eigenschaft zugänglich macht. Alles andere geht auch, aber es wäre eben keine Kapselung mehr.
WasWeißDennIch - Sa 23.02.13 14:59
Ich will ja nicht nerven, aber selbst bei einer ReadOnly-Objektproperty gibt es noch einen Pferdefuß: sie kann "von außen" freigegeben werden, ohne dass man das mitbekommen würde. Will man auch das verhindern, muss die Liste komplett verborgen und nur über definierte Schnittstellenmethoden zugänglich gemacht werden. Z.B. (ungetestet, direkt im Editor getippt):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| private FMyList: TStrings; function GetItem(Index: integer): string; procedure SetItem(Index: integer; const Value: string); public procedure AddItem(const Value: string); property Items[Index: integer]: string read GetItem write SetItem;
...
function TKlasse.GetItem(Index: integer): string; begin Result := FMyList[Index]; end;
procedure TKlasse.SetItem(Index: integer; const Value: string); begin FMyList[Index] := Value; end;
procedure TKlasse.AddItem(const Value: string); begin FMyList.Add(Value); end; |
Usw. usf.
Terra23 - Sa 23.02.13 15:14
Danke für eure rege Beteiligung und danke auch an Martok fürs Abtrennen vom anderen Thema. Im Gegensatz zu anderen Foren in denen ich aktiv war / bin, wird hier halt zu 100% auf Ordnung geachtet. ;)
Was mich interessiert: Warum ist es "böse", wie Wasweißdennich sagte, eine Liste als globale Variable zu deklarieren? Was ist denn der Unterschied zwischen einer globalen Variable und der Art von Deklaration über die wir in diesem Thread sprechen?
WasWeißDennIch - Sa 23.02.13 15:18
Globale Variablen gehören zu keiner Instanz und sind völlig "ungeschützt". Wenn Du mehrere Instanzen von TForm1 hast, gibt es trotzdem nur eine einzige globale Variable, das könnte für Chaos sorgen. Außerdem kann jeder, der herankommt, mit der Variablen anstellen, was er möchte. Bei einer Property hingegen steht die Liste unter der Kontrolle der Instanz, der sie gehört.
Bergmann89 - Sa 23.02.13 15:58
Hey,
kurz gesagt: Globale Variablen wiedersprechen einer objektorientierten Programmiereung. Hier und da muss man sie verwenden, aber das hat dann auch seine Gründe.
nochmal was zu dem OnCreate, OnShow, OnClose und OnDestroy: Wie schon gesagt initialisiert man Variablen und Objekte im OnCreate. OnCreate wird aufgerufen sobald das Objekt (die Form) erzeugt wurde. @Tranx: Das hat nix mit OnShow zu tun! OnDestroy ist das genaue Gegenteil zu OnCreate und wird aufgerufen, kurz bevor das Objekt freigegeben wird. OnShow wird aufgerufen wenn die Form angezeigt wird, und OnClose, wenn sie wieder geschlossen wird. OnCreate und OnDestroy bzw. OnShow und OnClose gehören sozusagen immer zusammen. Wenn man jetzt im OnCreate ein Objekt erzeugt, und im OnClose wieder freigibt, dann knallt es sobald das Fenster das zweite mal angezeigt wird. Bei den Standarteinstellungen der MainForm wird die Form nach dem Close freigegeben und kann demzufolge nicht mehr genutzt werden, aber das kann man auch verhindern und die Form z.B. in den Tray legen. Wenn sie dann wieder geöffnet wird, ist die Liste weg, weil diese ja im OnClose freigegeben wurde. Wenn ihr das wirklich im OnClose freigeben wollt, dann muss das auch im OnShow erzeugt werden. Das kommt dann darauf an was genau ihr damit bezwecken wollt. Bei OnShow/OnClose wird halt bei jedem neu öffnen des Fensters alles wieder initialisiert. Bei OnCreate/OnDestroy habt ihr solange das Programm läuft immer die gleiche Liste.
Ich hoffe ich konnte jetzt noch einige Missverständnisse klären :)
€: noch ne kleine Anmerkung zu den properties. Wenn die Get- bzw. Set-Methode die Liste nur zurück gibt bzw. setzt, dann brauch man keine extra Methode:
Delphi-Quelltext
1:
| property List: TStringList read fList write fList; |
MfG Bergmann.
WasWeißDennIch - Sa 23.02.13 16:22
Bergmann89 hat folgendes geschrieben : |
€: noch ne kleine Anmerkung zu den properties. Wenn die Get- bzw. Set-Methode die Liste nur zurück gibt bzw. setzt, dann brauch man keine extra Methode:
Delphi-Quelltext 1:
| property List: TStringList read fList write fList; | |
Das trifft zu, wenn die Instanz die Liste gar nicht selbst erzeugt, meinst Du das damit? Ansonsten wäre es falsch, siehe weiter oben.
Terra23 - Sa 23.02.13 17:46
Tranx hat folgendes geschrieben : |
Dann kannst Du mit einer Property auf die Liste zugreifen:
ANMERKUNG:
Wenn Du eine Stringlist so einbindest, dann darfst Du auf keinen Fall zwei Sachen machen:
in einer Prozedur oder Funktion außerhalb von TForm.Create und TForm.Destroy :
TStringlist.Create und TStringlist.Destroy
denn dann würdest Du eine existierende Liste neu erzeugen, oder eine zerstören. Und am Ende würde der Destruktor dann nichts (NIL) zerstören wollen, und das geht nun mal nicht. Das gibt dann einen Laufzeitfehler und Du kannst (normalerweise) das Programm nicht mehr beenden. Du kannst mit den Inhalten arbeiten, aber nie die Liste neu erzeugen oder zerstören. Bitte beim Einbinden und bei der Programmierung daran denken. |
Du redest hier aber von der immergleichen Liste, oder? Also wenn ich eine Liste global deklariert habe... Ich kann trotzdem noch in bestimmten Prozeduren weiterhin Listen verwenden, sofern sie nicht den gleichen Namen haben wie die global deklarierte Liste!?!
WasWeißDennIch hat folgendes geschrieben : |
Außerdem kann jeder, der herankommt, mit der Variablen anstellen, was er möchte. |
Wie meinst du das? Herankommen? Meinst du, wenn jemand von außen den Code eines bestehenden Programms "reproduziert"? Geht sowas?
WasWeißDennIch - Sa 23.02.13 18:21
Was ich meinte:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| unit Tralala;
interface
...
var MeineListe: TStringlist;
implementation
... |
Somit hat man aus allen Units, die Unit Tralala eingebunden haben, Zugriff auf MeineListe. Button2 in Form23 trägt da etwas ein, Button24 auf Form123 löscht Einträge heraus, Button12 auf Form2 gibt die Liste frei. Wie will man das noch überblicken?
Gerd Kayser - Sa 23.02.13 18:36
Bergmann89 hat folgendes geschrieben : |
Wie schon gesagt initialisiert man Variablen und Objekte im OnCreate. |
Ich machs meistens im Abschnitt initialization bzw Freigabe unter finalization.
Bergmann89 - Sa 23.02.13 19:46
@WasWeißDennIch: wieso is das falsch? Is doch genau das selbe wenn du ne Get und Set-Methode hast, die die Variable liest oder schreibt. Das wollt ich damit sagen.
@Gerd Kayser: aber keine objektabhängigen bzw. formabhängigen Variablen, oder?! :shock:
WasWeißDennIch - Sa 23.02.13 20:42
Für Objektproperties, die auch gesetzt werden dürfen, ist IMO ein Setter Pflicht, sofern die enthaltende Klasse die Instanz selbst anlegt. Innerhalb dieses Setters wird dann lediglich der Inhalt der übergebenen in die eigene Instanz kopiert. Wie ich weiter oben schon sagte: damit vermeidet man ein Speicherleck.
Martok - Sa 23.02.13 20:47
WasWeißDennIch hat folgendes geschrieben : |
Für Objektproperties, die auch gesetzt werden dürfen, ist IMO ein Setter Pflicht, sofern die enthaltende Klasse die Instanz selbst anlegt. Innerhalb dieses Setters wird dann lediglich der Inhalt der übergebenen in die eigene Instanz kopiert. Wie ich weiter oben schon sagte: damit vermeidet man ein Speicherleck. |
Das ist, mit Verlaub, Blödsinn.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| private fFoo: TFoo; public property Foo: TFoo read GetFoo write SetFoo;
procedure TTest.GetFoo: TFoo; begin Result:= fFoo; end;
procedure TTest.SetFoo(const aFoo: TFoo); begin fFoo:= aFoo; end; |
ist einfach nur die langsamere Version von
Delphi-Quelltext
1: 2: 3: 4:
| private fFoo: TFoo; public property Foo: TFoo read fFoo write fFoo; |
Unter Umständen kann man den Compiler da zum inlinen überreden, dann ist es zumindest nicht mehr langsamer. Schlechter zu lesen aber auf jeden Fall.
Gerd Kayser - Sa 23.02.13 20:47
Bergmann89 hat folgendes geschrieben : |
aber keine objektabhängigen bzw. formabhängigen Variablen, oder?! |
Für globale Variablen, z. B. für die Zusammensetzung von Dateinamen oder Verzeichnisnamen, die ich später überall
lesend brauche. Oder für das Anlegen, Öffnen und Schließen einer Protokolldatei. Oder für das Anlegen und Freigeben einer StringListe, die an einer Stelle mit Dateinamen gefüllt wird. An anderen Stellen holen sich weitere Funktionen dort ihre Informationen ab und löschen die abgearbeiteten Dateinamen aus der Liste raus. usw.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!