![]() |
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:
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.
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; 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:
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.
Procedure TMyForm.Button1Click (Sender : TObject);
Var B : TSomething; Begin B := TSomething.Create; Try B.Work; Finally B.Free End End; Bevor man hier mit weiteren Beispielen kommt, bitte nachdenken, ob es sich nicht um o.g. Securitylayer-Implementierungen handelt... |
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 ;-) |
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.
|
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.
|
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. |
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 -> ![]() Würde mich dann auch mal interessieren, ob Du mein Anliegen nachvollziehen kannst (zu dem ich immer noch stehe: -> ![]() 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. |
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. |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
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:
@DeddyH: Ich meine z.B. deinen Beitrag #2 in ![]() 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:
ist fast immer overkill.
MyStringList := TStringlist.Create;
Try MyStringList.Add('A lot of strings'); Finally MyStringList.Free; End; |
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? |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
|
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). |
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 ![]() Das Original hieß
Delphi-Quellcode:
Was eigentlich auch schon sicher genug ist. (siehe meine Erklärung dort drüben)
Result := '';
Parse := TStringList.Create; Row := TStringList.Create; try Parse.Text := GetBarcodesResult; 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? |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
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:
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.
MyStringList := TStringlist.Create;
<Zeile1> <zeile2> <Zeile3> MyStringList.Free; Zeig doch mal eine Code-Stelle aus einem deiner Programmen wo dich so ein try-finally so richtig nervt. |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
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. |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
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:
Anderes Beispiel: Grundsätzlich eine Variable initialisieren: Stört nicht, wird eh wegoptimiert und ist robust. Und überflüssig und blöd. Zitat:
Zitat:
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; |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
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. |
AW: Wieso Speicheranforderung in Try...Finally ?
Prima, jetzt werden wir konkret :thumb:
Zitat:
Delphi-Quellcode:
und wenn ich diesen Code sehe ...
b := TBar.Create;
try b.DoSomething(); finally b.Free end f := TFoo.Create; try f.Work(); finally f.free; end;
Delphi-Quellcode:
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.
f := TFoo.Create;
b := TBar.Create b.DoSomething(); f.Work(); b.Free; f.free; |
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. |
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; |
AW: Wieso Speicheranforderung in Try...Finally ?
Lös doch einmal eine Exception in einem der Objekte aus. Kommst Du dann noch bis zum Free?
|
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: |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
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:
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:
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. |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
Zitat:
Zitat:
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. |
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:
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 :)
A := TA.Create;
B := TB.Create; try DoSomething(); finally B.Free; A.Free; end; |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
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. |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
|
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. |
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:
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.
if Assigned(SomeObject) then
SomeObject.Free; |
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:
(das soll nur das grobe Konzept verdeutlichen - warum jetzt "false" im Fehlerfall das richtige Ergebnis ist, soll hier mal egal sein)
function myFunction (someParam: TsomeType): boolean;
... begin a:=TA.Create; try Result:=a.DoSomething; except Result:=false; end; a.Free; end; 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:
So.... bin mal auf eure Meinungen dazu gespannt. :) Bis denn Bommel |
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
|
AW: Wieso Speicheranforderung in Try...Finally ?
Zitat:
Bis denn Bommel |
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).
|
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"...
|
AW: Wieso Speicheranforderung in Try...Finally ?
Dann sehe ich das exakt genau wie Du. :thumb:
|
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.
|
AW: Wieso Speicheranforderung in Try...Finally ?
Sagen wir das nicht die ganze Zeit?
|
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. |
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; |
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.
|
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 11:29 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