Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Wieso Speicheranforderung in Try...Finally ? (https://www.delphipraxis.net/162214-wieso-speicheranforderung-try-finally.html)

FredlFesl 12. Aug 2011 18:07

Wieso Speicheranforderung in Try...Finally ?
 
Hi Gemeinde,

Hier wird einem ständig empfohlen, Speicheranforderungen und -freigabe unbedingt immer in Try-Finally-Blöcke zu packen ("Resourcenschutzblöcke")

Ich verstehe den Sinn nur bedingt. Natürlich ist es manchmal sinnvoll, aber eigentlich nur, um einen Securitylayer um eine Methode zu legen, à la:
Delphi-Quellcode:
Procedure TMyClass.QuiteAbstractMethod;
Begin
  helper := THelper.Create;
  Try
    Try
      helper.Perform;
      performSomethingMore;
    finally
      helper.free
    end;
  Except
    On E:Exception Do Begin
      UndoWhateverHappened;
      MapExceptionToAbstractLayer(E)
    End;
  End;
End;
Hier werden Exceptions einer tieferen Ebene so behandelt, das erstens die Methode nach außen hin keinen Schaden anrichtet und zweitens die sehr spezifischen Exceptions der tieferen Ebene in abstraktere Exceptions gemappt werden. Es interessiert z.B. den Anwender einer abstrakten Verbindungskomponente herzlich wenig, das die TCP/IP-Addresse nicht aufgelöst werden kann, schließlich weiß er gar nicht, das die Verbindung heute mal über TCP hergestellt wird.

Die Helper-Methode "Perform" wäre nach genau dem gleichen Muster gestrickt.

Kennt jemand noch andere sinnvolle Beispiele, wo ein "Resourcenschutzblock" wirklich nötig ist?

Versteht mich bitte nicht falsch, ich bau die Dinger seit Jahren automatisch ein, aber langsam nerven die Teile, denn sie machen den Code unübersichtlich und sind zum größten Teil überflüssig:

Delphi-Quellcode:
Procedure TMyForm.Button1Click (Sender : TObject);
Var
  B : TSomething;

Begin
  B := TSomething.Create;
  Try
    B.Work;
  Finally
    B.Free
  End
End;
Ich meine, wenn es hier knallt, hab ich einfach etwas falsch gemacht. Im Umkehrschluss benötige ich also kein Try-Finally, wenn ich alles richtig gemacht habe.

Bevor man hier mit weiteren Beispielen kommt, bitte nachdenken, ob es sich nicht um o.g. Securitylayer-Implementierungen handelt...

Phoenix 12. Aug 2011 18:15

AW: Wieso Speicheranforderung in Try...Finally ?
 
Es geht primär um größere Anforderungen. Ich hatte schon das ein oder mal, wo das einfache Erzeugen einer Klasse mehr Speicher allokieren wollte (bzw. sogar musste), als das System zu diesem Zeitpunkt bereitstellen konnte. So ne OutOfMemoryException ist böse, wenn sie einem dann den kompletten Prozess abschiesst anstelle sauber abgefangen und behandelt zu werden.

Deswegen sollte man immer damit rechnen, dass die Anforderung des Speichers schief gehen kann. Wenn man damit leben kann, das einem in dem Fall die Anwendung wegbricht ist das okay. Ich will das sauber abgefangen haben ;-)

DeddyH 12. Aug 2011 18:20

AW: Wieso Speicheranforderung in Try...Finally ?
 
Ich habe das Gefühl, dass Dir der Sinn des Exception-Handlings nicht ganz klar ist. Es ist ja nicht dazu da, um Fehler, die Du durch Schusseligkeit eingebaut hast, zu beheben, sondern um Ausnahmen (eben Exceptions) abzuhandeln. Es befreit einen nicht von der Pflicht, ggf. unumgängliche Ausgangszustände im Vorfeld abzuchecken. Tritt innerhalb eines try-finally-Blocks eine Exception auf, wird ja der finally-Teil in jedem Fall durchlaufen (sogar, wenn ein exit drinsteht), der except-Teil aber nur im Ausnahmefall. Verzichtet man auf Ressourcen-Schutzblöcke, dann hat man eben im Fehlerfall das Dilemma, dass der Code zum Freigeben nicht mehr ausgeführt wird.

Luckie 12. Aug 2011 18:33

AW: Wieso Speicheranforderung in Try...Finally ?
 
Genau wie mit dem Anschnallen im Auto. Auch wenn man sich anschnallt, sollte man möglichst Unfälle versuchen zu verhindern. Und auch wenn man zu 90% unfallfrei fährt, sollte man sich doch anschnallen.

Medium 12. Aug 2011 18:36

AW: Wieso Speicheranforderung in Try...Finally ?
 
In der Fachlogik nutze ich z.B. nur try..finally, und im finally wird nach Resourcenschutz und ggf. Undos die Exception wieder geraised. In der GUI bzw. GUI-nahen Teilen dagegen wenn möglich nur try..except, um ggf. entsprechende Meldungen ausgeben zu können. Der Resourceschutz sollte möglichst komplett in der Fachlogik passiert sein, ebenso will ich keine eine ShowMessage() aus diesen jemals sehen, denn da gehören sie nicht hin.

Sinn des finally ist halt eben genau das, wie es heisst: Resourcen schützen, sprich Speicherleichen vermeiden.

stahli 12. Aug 2011 19:02

AW: Wieso Speicheranforderung in Try...Finally ?
 
@FredlFesl

In mir hast Du einen Verbündeten. Aber Du musst Dich entscheiden, ob Du Dich mit dem Rest der DP-Welt anlegen willst :-D
-> http://www.delphipraxis.net/156651-b...erklaeren.html

Würde mich dann auch mal interessieren, ob Du mein Anliegen nachvollziehen kannst (zu dem ich immer noch stehe:
-> http://www.delphipraxis.net/160164-f...ewuenscht.html
Ich würde das für ein sehr nützliches Sprach-Feature halten.
Try-Finally-Blöcke haben auch ihren Nutzen, aber m.E. in anderen Anwendungsfällen.

himitsu 12. Aug 2011 19:34

AW: Wieso Speicheranforderung in Try...Finally ?
 
Und die "Anforderung" gehört NICHT in den Schutzblock, sondern direkt davor.
Ausnahme: Die entsprechende Variable wird vorher initialisiert und am Ende überprüft+freigegeben.

Geschützt (sicher freigegeben) werden die angeforderte Sachen, nach ihrer Anforderung und nicht die Anforderung selber,
denn geht diese schief, gibt's ja auch nix zum Freigeben.

FredlFesl 12. Aug 2011 20:39

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von Phoenix (Beitrag 1116520)
Es geht primär um größere Anforderungen...

Wenn das der Fall ist, kann die Applikation sowieso nicht weiterlaufen. Und wenn nicht, tritt wieder mein bisher einziger Fall ein, bei dem ein Try-Finally Sinn macht, nämlich einen Security-Layer/Wrapper. Hier nach dem Motto: "Schaun mer mal, obs reicht. Wenn nicht, dann eben nicht". Hier würde der EOutOfMemoryException in eine andere Exception gemappt werden bzw. würde das komplett transparent gehandhabt werden.

Nochmal: Ich weiss genau, wie Exceptions funktionieren und warum. Ich behaupte nur, das es nur diesen einen Fall für Try-Finally gibt und die Vorschläge (hier und in allen anderen Foren), GRUNDSÄTZLICH jedes "Free" in einen Finally-Block zu packen, Quatsch ist.

Zitat:

Zitat von Luckie (Beitrag 1116524)
Genau wie mit dem Anschnallen im Auto. Auch wenn man sich anschnallt, sollte man möglichst Unfälle versuchen zu verhindern. Und auch wenn man zu 90% unfallfrei fährt, sollte man sich doch anschnallen.

Aber beim Unfall ist die Fahrt doch vorbei... ;-) Verstehst Du? Tritt ein Fehler auf, gehts eh nicht weiterm AUSGENOMMEN mein Beispiel.

@DeddyH: Ich meine z.B. deinen Beitrag #2 in http://www.delphipraxis.net/162202-s...ring-list.html

Diese Regel "sollten immer"... ist overkill. Die Regel muss lauten: "Wenn Du einen reentranten Zustand wieder herstellen kannst, dann tu es".

Wenn z.B. der Speicher begrenzt ist, und(!) es an der Stelle nicht weiter geht, kann ich mir Resourcenschutzblöcke echt sparen. Mit dem Beenden der Anwendung wird der Speicher eh freigegeben.

Nochmal:
Code:
MyStringList := TStringlist.Create;
Try
  MyStringList.Add('A lot of strings');
Finally
  MyStringList.Free;
End;
ist fast immer overkill.

WM_CLOSE 12. Aug 2011 20:43

AW: Wieso Speicheranforderung in Try...Finally ?
 
Was aber, wenn das Programm ständig durchlaufen soll/muss? Ein Server vielleicht, oder eine Kontrollstation der Bahn?
Was passiert dann wohl, wenn sich das Program mal so eben verabschiedet?

Luckie 12. Aug 2011 20:46

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von FredlFesl (Beitrag 1116543)
Zitat:

Zitat von Luckie (Beitrag 1116524)
Genau wie mit dem Anschnallen im Auto. Auch wenn man sich anschnallt, sollte man möglichst Unfälle versuchen zu verhindern. Und auch wenn man zu 90% unfallfrei fährt, sollte man sich doch anschnallen.

Aber beim Unfall ist die Fahrt doch vorbei... ;-) Verstehst Du? Tritt ein Fehler auf, gehts eh nicht weiterm AUSGENOMMEN mein Beispiel.

Nicht wenn dein Leben das Programm ist, dann würdest du wahrscheinlich auch sehr darauf bedacht sein, dass du nach dem Unfall weiterlebst.

stahli 12. Aug 2011 21:03

AW: Wieso Speicheranforderung in Try...Finally ?
 
@FredFesl

So selbstbewußt hätte ich es nicht vortragen können, sehe das aber genau so.
Ansonsten müsste man (wenn man es genau machen wollte) beim Erzeugen von 10 Objekten nacheinander auch 10 verschachtelte Schutzblöcke erzeugen (für jedes Objekt einen).
Es kann ja jeder machen wie er will, aber der Sinn erschließt sich mir (in den meisten Fällen) nicht.


@WM_CLOSE+Luckie

Wenn ein Programm mit einem solch gravierenden Fehler wochenlang durchläuft, ist mit dem Ergebnis wohl ohnehin nix anzufangen.
Das gilt jedenfalls, wenn der Fehler nicht komplett bereigit wird (incl. aller Daten und Zustände).
Und diese Bereinigung kann man eigentlich nur gewährleisten, wenn man mit einem bestimmten Fehler rechnet, den man aber nicht unbedingt vermeiden kann (E/A-Funktionen).

himitsu 12. Aug 2011 21:23

AW: Wieso Speicheranforderung in Try...Finally ?
 
Ich sage es mal so "schau dir meinen FileSplitter (hier in der DP) mal an.
weiter siehe *1


Ja, wenn nach einem Knall (Exception) sowieso alles zu spät ist, könnte man es auch gleich sein lassen ... Windows räumt ja auf.

Aber dennoch sollte man es in einem gewissen Maße machen, denn wenn man etwas immer gleich macht, egal ob das Programm wichtig oder nutzlos ist, dann hat man a) ähnliche Programmstrukturen (Wiederverwendbarkeit) und damit auch ein leichteres Arbeiten, da man sich nur an eine Struktur halten muß, was auch das Lesen/Verstehen vereinfacht
und wenn man dann durch das viele Programmieren dieses total verinnerlicht hat, dann kann man es an wichtigen stellen nicht so schnell vergessen.



*1 Ja, auch ich schreibe schonmal Programme ganz ohne Try-Finally, denn unter gewissen Umständen ist sowas garnicht nötig, da z.B. die WinAPI an vielen Stellen mit Fehlercodes arbeitet und nicht mit Exceptions.
Wenn es dort Probleme gibt, dann werden diese nicht gleich mit einer Exception quitiert und man kann/muß diese Fehler dann anderes behanden. (knallt es dort wirklich mal mit einer Exception durch, dann ist sowieso alles zu spät und jede weitere Bemühung eher umsonst)


Mit genügend Wissen/Erfahrung kennt man gewisse Schwachstellen oder auch Ecken, wo eigentlich nie was schief laufen kann ... in soeinem Kall kann man die Fehlerbehandlung auch schonmal etwas "anpassen".

Aber im Normalfall sollte man einfach alles Schützen, wenn man weiß ja nie was mal passiert ... ganz besonders bei Codes, welche man anderen zur Verfügung stellt.
siehe http://www.delphipraxis.net/162202-s...ml#post1116391
Das Original hieß
Delphi-Quellcode:
Result := '';
Parse := TStringList.Create;
Row := TStringList.Create;
try
  Parse.Text := GetBarcodesResult;
Was eigentlich auch schon sicher genug ist. (siehe meine Erklärung dort drüben)
Aber dennoch, ein gewisser (ausreichender) Schutz wird dennoch verbaut, auch wenn einige meinen könnten "wozu ... ist doch eh umsonst?".
Denn weißt du, ob der Code nicht mal in einem wichtigen Programm verbaut würde?

HeZa 13. Aug 2011 02:43

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von FredlFesl (Beitrag 1116543)
Nochmal:
Code:
MyStringList := TStringlist.Create;
Try
  MyStringList.Add('A lot of strings');
Finally
  MyStringList.Free;
End;
ist fast immer overkill.

Hm... diese Aussage ist ziemlich zweckfrei. Wenn du 6 Zeilen sinnlosen Code nimmst, bringt dich die Aussage, dass 3 davon überflüssig sind auch nicht weiter. :?

Aber du suchst ja nach einer Erklärung warum "immer" try-finally verwenden sollte.

1. Grund. Warum denn nicht?
Ein vergessenes try-finally kann ein Fehler sein, aber ein try-finally, dass nicht notwendig ist, ist niemals falsch. vielmehr macht es meinen Code robuster (z.B. für Erweiterungen oder für Verwendung an anderer Stelle). Meine Erfahrung: Da wo man das try-finally weglassen könnte, da stört es nicht und da wo es stört, kann man es nicht weglassen.

2. Grund: Lesbarkeit
Finde ich im Code eine Stelle an der eine Resource belegt oder ein Objekt erzeugt wird, will ich sofort als nächstes wissen wo die Freigabe ist. Ein try-finally ist da eine schöne Klammer die mich zu der Stelle mit der Freigabe führt.

3. Grund: Leichtes überprüfen auf Korrektheit
Um sagen zu können, dass Code in der Form
Code:
MyStringList := TStringlist.Create;
<Zeile1>
<zeile2>
<Zeile3>
MyStringList.Free;
kein Speicherleck produziert, muss ich für alle Zeilen dazwischen garantieren, dass diese keine Exceptionen werfen. Sobald da eine Prozedure aufgerufen wird, ist das keineswegs trivial.

Zeig doch mal eine Code-Stelle aus einem deiner Programmen wo dich so ein try-finally so richtig nervt.

samso 13. Aug 2011 07:01

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von stahli (Beitrag 1116549)
Ansonsten müsste man (wenn man es genau machen wollte) beim Erzeugen von 10 Objekten nacheinander auch 10 verschachtelte Schutzblöcke erzeugen (für jedes Objekt einen).

Verstehe ich nicht, in dem Thread "Bitte Warning erklären" wurden doch bereits Lösungen gezeigt, wie man die Verschachtelungen vermeiden kann. Auch die Behauptung "Wenn eine Exception auftritt, kann man alle weiteren Berechnung danach vergessen" stimmt doch so pauschal nicht. Das hängt doch stark von dem Kontext ab. Programme der Kategorie 24/7 haben da sicherlich andere Ansprüche, als ein Kommandozeilentool, dass nur mal schnell für eine spezifische Aufgabe zusammen gestrickt wurde.

Bei mir ist es eigentlich eher umgekehrt. Wenn in meinen Quelltext mal ein Schutzblock fehlt, dann ist das bei einer späteren Inspektion des Quelltextes immer ein Punkt, bei dem ich dann u.U. länger nachdenke, ob das an dieser Stelle tatsächlich legitim ist. D.h. ich verwende eventuell mehr Zeit bei der Wartung eines Quelltextes, wenn der Schutzblock fehlt, als wenn ich ihn konsequent einbaue.

D.h. der Schutzbock um "b.work" in dem Eingangs genannten Beispiel ist schon deshalb sinnvoll, weil ich bei der Wartung nicht in b.work reingehen muss um zu prüfen, ob da auch wirklich keine Exception auftreten kann.

Impliziert das Wort "Schutzblock" nicht schon, dass es sich um einen Security-Layer handelt? Und nein, ich kann mir keinen anderen sinnvollen Einsatz von Try-finally-Blöcken vorstellen, als genau diesen. Letztendlich ist es also die Frage, will ich auf Nummer sicher gehen, oder glaube ich, dass ich und alle anderen Verkehrsteilnehmer so gut sind, das niemals ein Unfall passieren kann (bzw. die Konsequenzen eines Unfalls sind mir einfach egal). Das muss jeder für sich entscheiden.

FredlFesl 13. Aug 2011 08:41

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von Luckie (Beitrag 1116545)
...Nicht wenn dein Leben das Programm ist, dann würdest du wahrscheinlich auch sehr darauf bedacht sein, dass du nach dem Unfall weiterlebst.

Da haben wir es!
Wenn "mein Leben" das Programm ist, dann benötige ich den Sicherheitsgurt, denn das Fahren ist nur eine Aktion, die kontrolliert schiefgehen können muss.

Ist "die Fahrt" mein Programm, dann muss ich mich nicht anschnallen. Peng => Programm-Neustart. Wieso muss ich einen Sicherheitsgurt tragen, wenn ich bei einem Unfall sowieso von Vorne anfange?


Zitat:

Zitat von HeZa (Beitrag 1116563)
Zitat:

... ist fast immer overkill.
Hm... diese Aussage ist ziemlich zweckfrei. Wenn du 6 Zeilen sinnlosen Code nimmst, bringt dich die Aussage, dass 3 davon überflüssig sind auch nicht weiter. :?

Eben doch. Der code ist nicht sinnlos, sondern aus einem Beitrag, in dem die Verwendung der TStringlist erklärt wurde. In [b]diesem[b/] Schnippsel ist Try-Finally überflüssig.

Zitat:

Aber du suchst ja nach einer Erklärung warum "immer" try-finally verwenden sollte.
1. Grund. Warum denn nicht?
Stimmt, aber es ist überflüssig. Aber guter Code ist minimalistisch. Aus dem gleichen Grund verwende ich "FreeAndNil" nicht. Ich will in meinem Code nicht gefragt werden: "Wieso machst Du das denn hier? Das ist überflüssig"
Anderes Beispiel: Grundsätzlich eine Variable initialisieren: Stört nicht, wird eh wegoptimiert und ist robust. Und überflüssig und blöd.
Zitat:

2. Grund: Lesbarkeit
Finde ich im Code eine Stelle an der eine Resource belegt oder ein Objekt erzeugt wird, will ich sofort als nächstes wissen wo die Freigabe ist. Ein try-finally ist da eine schöne Klammer die mich zu der Stelle mit der Freigabe führt.
Wieso ist es wichtig, wo die Freigabe erfolgt? Ist doch komplett egal (außer, ich suche ein Speicherleck). Bei C# und Java suchst Du dich i.A. ja auch blöd, um die Freigabe eines Objektes zu finden.
Zitat:

Zeig doch mal eine Code-Stelle aus einem deiner Programmen wo dich so ein try-finally so richtig nervt.
Delphi-Quellcode:
  f := TFoo.Create;
  Try
    b := TBar.Create
    Try
      b.DoSomething();
      f.Work();
    Finally
      b.Free
    End
  Finally
    f.free;
  End;
// --- vs
 
  f := TFoo.Create;
  b := TBar.Create
  b.DoSomething();
  f.Work();
  b.Free
  f.free;

FredlFesl 13. Aug 2011 08:48

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von himitsu (Beitrag 1116552)
und wenn man dann durch das viele Programmieren dieses total verinnerlicht hat, dann kann man es an wichtigen stellen nicht so schnell vergessen....

*1 Ja, auch ich schreibe schonmal Programme ganz ohne Try-Finally, denn unter gewissen Umständen ist sowas garnicht nötig,...
Mit genügend Wissen/Erfahrung kennt man gewisse Schwachstellen oder auch Ecken, wo eigentlich nie was schief laufen kann ...

Aber im Normalfall sollte man einfach alles Schützen, wenn man weiß ja nie was mal passiert ... ganz besonders bei Codes, welche man anderen zur Verfügung stellt.

Also wird das Try-Finally immer angegeben, obwohl wir alle wissen, das es in diesem konkreten Beispiel vielleicht überflüssig ist, aber, hey, sicher ist sicher und außerdem schauen Kinder zu und da konnen wir nicht einfach sagen das man sich manchmal nicht schützen muss.

So, wie wenn wir immer erzählen, das man die Ampel nur bei 'grün' überqueren darf, aber: -unter uns-, wenn sonst keiner da ist, WTF.

HeZa 13. Aug 2011 10:28

AW: Wieso Speicheranforderung in Try...Finally ?
 
Prima, jetzt werden wir konkret :thumb:
Zitat:

Zitat von FredlFesl (Beitrag 1116574)
Delphi-Quellcode:
  f := TFoo.Create;
  Try
    b := TBar.Create
    Try
      b.DoSomething();
      f.Work();
    Finally
      b.Free
    End
  Finally
    f.free;
  End;
// --- vs
 
  f := TFoo.Create;
  b := TBar.Create
  b.DoSomething();
  f.Work();
  b.Free
  f.free;

also das würde ich schon mal umbauen zu:
Delphi-Quellcode:
  b := TBar.Create;
  try
    b.DoSomething();
  finally
    b.Free
  end

  f := TFoo.Create;
  try
    f.Work();
  finally
    f.free;
  end;
und wenn ich diesen Code sehe ...
Delphi-Quellcode:
  f := TFoo.Create;
  b := TBar.Create
  b.DoSomething();
  f.Work();
  b.Free;
  f.free;
sehe ich 2 potenzielle Speicherlöcher. Um sicher zu gehen, dass der Code keine Speicherlöcher erzeugt, muss ich nun prüfen, ob TBar.Create, b.DoSomething und f.Work keine Exceptions werfen. Falls ich Glück habe und TBar.Create erstellt, wie ich anhand der Namen vermute, tatsächlich ein neues Objekt, bin ich allerdings schnell damit fertig, weil dann eine EOutOfMemory-Exception geworfen werden könnte und damit wäre der Code für mich definitiv gefährlich.

Uwe Raabe 13. Aug 2011 10:34

AW: Wieso Speicheranforderung in Try...Finally ?
 
Ich hätte da auch noch etwas Senf dazu: Es mag ja durchaus sein, daß zum Zeitpunkt der Programmierung das Freigeben eines Objekts auch nach einer Exception ziemlich egal ist. Das muss aber nicht für immer so sein. Ist zwar etwas konstruiert, aber man stelle sich vor, daß das Objekt eine externe Resource belegt (z.B. eine Server-Verbindung aufbaut, einen COM-Server startet oder einen Lizenzzähler hochzählt), die unter allen Umständen wieder freigegeben werden muss.

Der Punkt dabei ist, daß dieses Verhalten vielleicht erst später eingebaut wird oder womöglich erst zur Laufzeit bestimmt wird. Dann möchte man ja auch nicht alle Instanzen in allen Programmen durchgehen, ob die denn auch schön brav in ein try-finally geklammert sind.

Insofern ist ein try-finally Block etwas, daß mir womöglich meine zukünftige Arbeit erleichtert und mich vor meinen eigenen Fehlern schützt.

stahli 13. Aug 2011 10:48

AW: Wieso Speicheranforderung in Try...Finally ?
 
Wie sähe es denn so aus?

Delphi-Quellcode:
  f := TFoo.Create;
   b := TBar.Create
   b.DoSomething();
   f.Work(b);
   b.DoOther;
   b.Free
   f.free;

DeddyH 13. Aug 2011 11:02

AW: Wieso Speicheranforderung in Try...Finally ?
 
Lös doch einmal eine Exception in einem der Objekte aus. Kommst Du dann noch bis zum Free?

stahli 13. Aug 2011 11:16

AW: Wieso Speicheranforderung in Try...Finally ?
 
Nein, natürlich nicht.
- Das Programm ist fehlerhaft und muss korrigiert werden.
- Es ist unklar, was in f.Work und b.DoOther nun genau passiert ist und inwieweit Ergebnisse aus diesen Methoden noch brauchbar sind.

Diese beiden Punkte träfen aber auch zu, wenn die Aufrufe in try-Blöcke gekapselt wären.
Lediglich die zwei Pointer würden dann halt nicht mehr im Speicher rumdümpeln.

In sehr kritischen Zusammenhängen oder bei Referenzzählungen, externen Speicheranforderungen (@Uwe: ich hatte keinen roten Kasten vorhin) o.ä. würde ich ja auch Schutzblöcke einsetzen.
Aber nicht, wenn ich mir mal ein blödes Objekt erzeuge, und das kurz darauf wieder freigebe.

Code halte ich auch nicht für besser lesbar, wenn überall try-Blöcke verwendet werden.

Ich will ja niemanden missionieren, wollte nur ursprünglich mal nachfragen, wozu das Ganze und sagen, dass ich das halt so pauschal (immer noch) nicht nachvollziehen kann.


Aber ich habe Euch trotzdem alle ganz doll lieb - das muss auch mal wieder gesagt werden. :love:

jaenicke 13. Aug 2011 11:50

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von FredlFesl (Beitrag 1116574)
Aber guter Code ist minimalistisch.

Vor allem aber robust. Was nützt es mir, wenn ein Programmcode 10 Zeilen weniger hat, dafür aber beim kleinsten Problem nicht mehr macht was er soll?

Was bei Speicherlecks passiert, konnte man in einigen Versionen vom Firefox sehr gut sehen. Oder auch in Delphi 2005. Und auch wenn euch beiden das anscheinend anders geht (ich weiß ja nicht, ob ihr 32 GiB RAM habt oder so...), aber 99% der Benutzer hat es durchaus gestört, wenn diese Programme im Laufe der Zeit viele hundert MiB RAM belegt haben. Bei ein paar Stunden Laufzeit waren es bei mir schonmal 2 GiB oder so, so dass wegen 32 Bit Schluss war und das Programm nicht mehr ging... :roll:

Ich möchte mit meinem PC produktiv arbeiten, dementsprechend kann ich mit Software, die Speicherlecks hat und meinen RAM vollmüllt, nicht so viel anfangen.

Zitat:

Zitat von FredlFesl (Beitrag 1116574)
Aus dem gleichen Grund verwende ich "FreeAndNil" nicht. Ich will in meinem Code nicht gefragt werden: "Wieso machst Du das denn hier? Das ist überflüssig"

Gerade FreeAndNil ist an vielen Stellen sehr wichtig. Nämlich immer dann, wenn man später wissen will, ob das Objekt schon erzeugt wurde. Auch ein zweiter Aufruf an Free knallt sonst.

Ein Beispiel ist hier das Singleton-Pattern. Wie willst du denn wissen, ob das Objekt existiert, wenn du es nicht auf nil prüfen kannst?

Zitat:

Zitat von FredlFesl (Beitrag 1116574)
Anderes Beispiel: Grundsätzlich eine Variable initialisieren: Stört nicht, wird eh wegoptimiert und ist robust. Und überflüssig und blöd.

Wenn eine lokale Variable nicht initialisiert wird, ist es russisches Roulette was dabei herauskommt.

Felder eines Objektes und globale Variablen werden automatisch initialisiert, so dass hier eine Initialisierung nur notwendig ist, wenn ein anderer Wert als 0, False, ... als Startwert benötigt wird. Besser lesbar sind definierte Anfangswerte aber definitiv, da man sonst immer erst schauen muss, ob vielleicht irgendwo anders noch etwas initialisiert wird.

FredlFesl 15. Aug 2011 13:38

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von jaenicke (Beitrag 1116598)
Zitat:

Zitat von FredlFesl (Beitrag 1116574)
Aus dem gleichen Grund verwende ich "FreeAndNil" nicht. Ich will in meinem Code nicht gefragt werden: "Wieso machst Du das denn hier? Das ist überflüssig"

Gerade FreeAndNil ist an vielen Stellen sehr wichtig. Nämlich immer dann, wenn man später wissen will, ob das Objekt schon erzeugt wurde. Auch ein zweiter Aufruf an Free knallt sonst.

Korrekt, aber dann ist mein Programm ja falsch. Ein fehlerfreies Programm benötigt kein FreeAndNil!
Zitat:

Zitat von jaenicke (Beitrag 1116598)
Ein Beispiel ist hier das Singleton-Pattern. Wie willst du denn wissen, ob das Objekt existiert, wenn du es nicht auf nil prüfen kannst?

Hmmm. Bei meinem Singleton-Pattern brauche ich das nicht und wenn, wäre es ein sinnvoller Einsatz. Ich sag ja nicht, das man es NIE benötigt, sondern nur NICHT IMMER. TRY-FINALLY braucht man auch, aber NICHT IMMER...

Zitat:

Zitat von jaenicke (Beitrag 1116598)
Zitat:

Zitat von FredlFesl (Beitrag 1116574)
Anderes Beispiel: Grundsätzlich eine Variable initialisieren: Stört nicht, wird eh wegoptimiert und ist robust. Und überflüssig und blöd.

Wenn eine lokale Variable nicht initialisiert wird, ist es russisches Roulette was dabei herauskommt.

Bitte lesen... "wird eh wegoptimiert" gilt ja wohl nicht für deinen Fall.

Ich rede von Grundsätzlich und Du kommst mit einzelnen Beispielen, wo es sinnvoll ist. Tut mir leid, das widerlegt meine Behauptung nicht ("Ausnahmen bestätigen die Regel"). Und: Ja natürlich gibt es Szenarien, wo ein FreeAndNil oder eine Variableninitialisierung wichtig ist. Ich bin ja nicht von Gestern.

Nur lese ich oft, das man immer FreeAndNil verwenden sollte, weil es ja nicht schadet. So ein Blödsinn.
Genauso blödsinnig wie: Variablen müssen immmer, d.h. ohne Nachdenken, initialisiert werden. Quatsch.
Oder eben: Create...Free (sorry: FreeAndNil) gehört immer zusammen mit einem Try-Finally.

Mich stört das "immer", weil es gleichbedeutend ist mit "ohne Nachdenken".

Klar: Ist immer noch besser als die Aussage: "Vergiss Try-Finally, das ist nur was für Angsthasen".

Also: In meinen Anwendungen steht dort, wo es nötig ist (und nur dort) Try-Finally.
FreeAndNil verwende ich nicht, denn meine Objekte werden nicht doppelt freigegeben. Letzteres habe ich mir so angewöhnt, es mag eine Marotte sein, aber seit dem laufen die Programme einfach besser: Vor allen Dingen sagt mir FastMM4, ob ich nicht aufgepasst habe.
Variablen werden nur dort initialisiert, wo es sinnvoll ist. Ich prüfe meine Anwendungen bis zum Release immer mit FastMM4, d.h. die ersten paar Monate laufen sie mit allen Prüfungen von FastMM. Erst wenn nix mehr passiert, deaktiviere ich die Reporting-Features.

[/QUOTE]Besser lesbar sind definierte Anfangswerte aber definitiv[/QUOTE] Das lasse ich gelten.

Neutral General 15. Aug 2011 14:07

AW: Wieso Speicheranforderung in Try...Finally ?
 
Hallo,

Ich verwende auch nicht immer FreeAndNil. Auch nur wenn ich an anderer Stelle wissen muss ob das Objekt noch existiert - Und das hat nicht zwangsweise etwas mit schlechter Programmierung zu tun. Es ist manchmal einfach notwendig. Bei lokalen Objekten verwende ich FreeAndNil deswegen eben nicht. Bringt ja nix in den Stack ne 0 reinzuschreiben, die 10ms später wieder durch was anderes überschrieben wird :mrgreen:

try-finally verwende ich allerdings immer. Ich hab es mir so angewöhnt und man ist einfach auf der sicheren Seite. Abgesehen davon finde ich sogar, dass try-finally Code auch um einiges schöner/strukturierter aussieht.
Bin der Meinung dass die Vorteile von try-finally einfach überwiegen. Das Programm wird vllt. 3 Byte größer und 1ms langsamer aber das ist es mir dann (allein schon aus oben genannten optischen Gründen) einfach wert.

Allerdings muss ich sagen, dass ich (meistens) auch kein Hardcore-Try-Finally Mensch bin. Also ich bin auch ab und zu mal etwas "großzügiger" und schreib sowas:
Delphi-Quellcode:
A := TA.Create;
B := TB.Create;
try
  DoSomething();
finally
  B.Free;
  A.Free;
end;
Denn zuuu verschachtelt ist mir dann doch meistens zu unleserlich. Aber so Fälle gibt es eigentlich eher selten. Meistens sind die try-finallys ja hintereinander :)

jaenicke 15. Aug 2011 14:30

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von FredlFesl (Beitrag 1116990)
Zitat:

Zitat von jaenicke (Beitrag 1116598)
Auch ein zweiter Aufruf an Free knallt sonst.

Korrekt, aber dann ist mein Programm ja falsch. Ein fehlerfreies Programm benötigt kein FreeAndNil!

Nein, das ist nicht falsch. Es ist wie du so schön sagtest minimalistisch. Warum sollte ich mir extra noch einen Status mitführen, ob ein Objekt noch existiert. Wenn ich FreeAndNil benutzt habe, kann ich das einfach am Ende noch einmal hinschreiben. Ist es schon freigegeben, umso besser, wenn nicht, passiert es dort.

Wenn man 100%ige Kontrolle über den Programmfluss hat, braucht man das natürlich nicht, aber das ist eben nicht immer der Fall.

Ich benutze FreeAndNil bei Feldern eines Objekts immer. Einfach weil es die Fehlersuche erheblich vereinfacht, wenn ich weiß, dass in Speicherdumps oder ähnlichem alle schon freigegebenen Objektzeiger auch wirklich nil sind. Und es macht die Fehleranalyse auch einfacher, wenn ich weiß, dass eine Speicherschutzverletzung an einer höheren Adresse in keinem Fall von einem schon freigegebenen Objekt stammen kann.

Uwe Raabe 15. Aug 2011 15:22

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von FredlFesl (Beitrag 1116990)
Ein fehlerfreies Programm benötigt kein FreeAndNil!

Blödsinn! Das hat nichts mit Fehlern zu tun, sondern lediglich mit Programmdesign - und das ist ja bekanntlich Geschmackssache.

Insider2004 15. Aug 2011 15:33

AW: Wieso Speicheranforderung in Try...Finally ?
 
FreeAndNil macht nur bei globalen Instanzen Sinn.

function xyz;
var
A: ...
begin
...
A.Free;
end;

Hier z.b. wäre es Blödsinn. Da laufe ich sowieso aus dem Scope.

DeddyH 15. Aug 2011 15:37

AW: Wieso Speicheranforderung in Try...Finally ?
 
Richtig, aber andererseits würde es auch nicht wirklich stören. Was man auch häufiger sieht:
Delphi-Quellcode:
if Assigned(SomeObject) then
  SomeObject.Free;
Die Assigned-Abfrage ist genauso überflüssig, aber ich würde nicht hergehen und sie löschen, weil ich es einfach nicht ertragen kann oder die Stimmen in meinem Kopf es mir befehlen.

Bbommel 16. Aug 2011 10:37

AW: Wieso Speicheranforderung in Try...Finally ?
 
Hallo zusammen,

ich habe mir diesen ganzen und die verlinkten Threads mal sehr interessiert durchgelesen, weil ich auch schon seitdem ich hier mitlese (und manchmal schreibe) nicht nachvollziehen kann, wieso man so auf dieses try...finally pocht. Auch die Argumente, die bisher hier vorgebracht wurden, konnten mich nicht so richtig überzeugen - die Gegenargumente haben mir hingegen teilweise aus der Seele gesprochen. :)

Warum ich mich hier noch mal melde, obwohl schon ziemlich viel zu dem Thema geschrieben wurde, ist, dass mir ein Aspekt bisher viel zu kurz kam, auch wenn er durchaus schon erwähnt wurde - vielleicht bringt das ja noch mal neue Punkte auf, das try/finally-Gebot zu verstehen. Mir geht es um den Vergleich von try/except und try/finally.

Als Argument für try/finally wird immer wieder geschrieben, dass es das Programm robuster machen würde, weil so auch unerwartete Fehler behandelt würden. Meiner Einschätzung nach macht try/finally (zumindest so, wie es hier verwendet wird) aber genau diese Fehlerbehandlung eben nicht. Im finally-Block werden nur diejenigen Dinge aufgeräumt, die auch im Erfolgsfall aufgeräumt werden würden - das ist natürlich weniger schlecht, als würde man das auch noch lassen, aber eine Fehlerbehandlung ist das ja nicht. Will sagen: zwar hat man den Speicher wieder ordentlich freigegeben, die Funktion selbst und damit ggf. das Programm insgesamt sind aber in einem undefinierten Zustand. Bei den als Parade-Argument für die try-finally-Sache vorgebrachten ständig laufenden Hintergrund-Anwendungen ist das doch eigentlich viel kritischer.

Viel sauberer finde ich daher eigentlich statt des hier meist geschriebenen fast immer:
Delphi-Quellcode:
function myFunction (someParam: TsomeType): boolean;
...
begin
  a:=TA.Create;
  try
    Result:=a.DoSomething;
  except
    Result:=false;
  end;
  a.Free;
end;
(das soll nur das grobe Konzept verdeutlichen - warum jetzt "false" im Fehlerfall das richtige Ergebnis ist, soll hier mal egal sein)

Klar, wenn man den Fehler nach außen durchreichen will, muss man ihn halt erneut raisen und das ganze dann ggf. mit lustigen try/except/finally-Verschachtelungen versehen. Aber nur mit try-except hat man tatsächlich eine echte Fehlerbehandlung und auch hier wird übrigens im Fehlerfall aufgeräumt.

Meiner Meinung nach also:
  • Wenn es tatsächlich um eine (kritische) Anwendung geht, die ggf. auch noch unattended vor sich hinwerkeln soll, bringen nur try/except- oder try/except/finally tatsächlich eine Fehlerbehandlung. Ein einfaches try/finally mag weniger schlimm sein als gar nix, aber das Programm läuft potentiell dennoch amok.
  • In einer GUI-Anwendung sollte man meiner Meinung nach die bekannten kritischen Stellen mit try/except-Blöcken versehen, um auch dem Benutzer im Fehlerfall ein sauberes Feedback geben zu können (falls nötig) oder das Problem intern abzufangen und zu behandeln. Sollte bei einer nicht-kritischen GUI-Anwendung an einer eigentlich völlig harmlosen Stelle eine Exception auftreten (z.B. der mal erwähnte Hardware-Schaden), dann hat das Gesamtsystem aber ganz andere Probleme, bei denen ich nicht weiß, warum sich mein Programm darum kümmern sollte.

So.... bin mal auf eure Meinungen dazu gespannt. :)

Bis denn
Bommel

DeddyH 16. Aug 2011 10:42

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von Bbommel (Beitrag 1117184)
Als Argument für try/finally wird immer wieder geschrieben, dass es das Programm robuster machen würde, weil so auch unerwartete Fehler behandelt würden.

Quellen?

Bbommel 16. Aug 2011 10:50

AW: Wieso Speicheranforderung in Try...Finally ?
 
Zitat:

Zitat von DeddyH (Beitrag 1117185)
Zitat:

Zitat von Bbommel (Beitrag 1117184)
Als Argument für try/finally wird immer wieder geschrieben, dass es das Programm robuster machen würde, weil so auch unerwartete Fehler behandelt würden.

Quellen?

Na, dieser Thread. :) So hab ich hier die Hauptargumente für das try/finally jedenfalls verstanden: Auch im Fehlerfall werden die Ressourcen noch sauber freigegeben => das Programm wird robuster. Das Wort robuster schwirrte mir im Kopf rum, weil es HeZa in Beitrag #13 benutzt hat. Passt aber ganz gut zu dem, wie ich die Pro-Argumente bisher verstanden habe.

Bis denn
Bommel

DeddyH 16. Aug 2011 10:52

AW: Wieso Speicheranforderung in Try...Finally ?
 
Ich meinte Quellen für den 2. Teil: unerwartete Fehler werden nur bei try-finally behandelt (zumindest habe ich das so rausgelesen).

Bbommel 16. Aug 2011 11:03

AW: Wieso Speicheranforderung in Try...Finally ?
 
Ähm, nee, so war das nicht gemeint... Kann man das so lesen? Hmmm... ungeschickt formuliert vielleicht. Streiche das "so auch"...

stahli 16. Aug 2011 11:13

AW: Wieso Speicheranforderung in Try...Finally ?
 
Dann sehe ich das exakt genau wie Du. :thumb:

Luckie 16. Aug 2011 12:01

AW: Wieso Speicheranforderung in Try...Finally ?
 
Dass try-finally Fehler behandelt ist falsch. try-finally ist ein Ressourcenschutzblock und auch im Fehlerfall belegte Ressourcen wieder frei zu geben. Eine Fehlerbehandlung findet im Except-Block statt.

DeddyH 16. Aug 2011 12:07

AW: Wieso Speicheranforderung in Try...Finally ?
 
Sagen wir das nicht die ganze Zeit?

Luckie 16. Aug 2011 12:16

AW: Wieso Speicheranforderung in Try...Finally ?
 
Wir ja, aber stahli in seinem letzten Posting nicht.

Und ein Ressourcenschutzblock macht eine Anwendung in sofern robuster, dass in einem Fehlerfall der Speicher nicht zu läuft. Nehmen wir an man bearbeitet in einer Schleife hunderte von Objekten (Bilder zum Beispiel). Jetzt kommt es bei mehreren Bildern zu Fehlern, warum auch immer. Habe ich jetzt keinen Ressourcenschutzblock, würde der allozierte Speicher nicht mehr freigegeben. Und das kann durch aus zu kritischen Situationen führen. Oder anderes Beispiel: Ich schreibe ein Programm welches Ressourcen benötigt, die nicht immer unbedingt verfügbar sind, weil der Server weggebrochen ist. Da haben wir wieder das gleiche Problem. Nicht immer muss ein Programm unbrauchbar sein, wenn mal eine Ressource nicht zur Verfügung steht.

DeddyH 16. Aug 2011 12:21

AW: Wieso Speicheranforderung in Try...Finally ?
 
Quizfrage: wird Test freigegeben?
Delphi-Quellcode:
type
  TTest = class
  public
    procedure DoWork;
  end;

procedure TTest.DoWork;
begin
  raise Exception.Create('Kabumm');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Test: TTest;
begin
  Test := TTest.Create;
  try
    Test.DoWork;
  except
    on E: Exception do
      begin
        ShowMessage('Irgendeine Fehlerbehandlung');
        raise;
      end;
  end;
  Test.Free;
end;

Bbommel 16. Aug 2011 12:36

AW: Wieso Speicheranforderung in Try...Finally ?
 
Nein, wird es nicht, deswegen hatte ich ja auch geschrieben, dass man bei einem re-raise der Exception mit verschachteltem try-except-try-finally arbeiten müsse. Ohne das erneute raise würde es aber freigegeben.

DeddyH 16. Aug 2011 12:42

AW: Wieso Speicheranforderung in Try...Finally ?
 
Richtig. Stellen wir uns nun vor, wir haben eine Klasse mit so einer Methode drin (sagen wir ca. 20 LOC). Irgendwann denken wir uns, dass es evtl. ganz gut wäre, die Exception durchzureichen. Gesagt, getan, ein "raise" ans Ende des Except-Blocks und fertig. Schon haben wir uns Platz für ein Speicherleck geschaffen und merken es nicht bzw. können uns nicht erklären, wo es herkommt. Zumindest ich würde nicht als Erstes daran denken, noch einen try-finally-Block drumherum zu schreiben, schließlich war das Leck vorher ja auch nicht da. Und nun erteile ich der anderen Partei das Wort, die finally aus Übersichtlichkeitsgründen weglässt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:40 Uhr.
Seite 1 von 2  1 2      

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