Ja, danke Sebastian.
Deinen Text kann ich nachvollziehen und da gehe ich auch weitestgehend mit.
Dein Beispiel ist grundsätzlich auch in Ordnung, da Du eine
Exception auswertest und darauf reagierst.
Oft sieht man aber Beispiele wie Dein "Run" alleinstehend, wo die Objekte wieder freigegeben werden und keine Fehlerbehandlung ersichtlich ist.
Das finde ich dann halt unsinnig.
Folgenden Punkt Deiner Ausführungen sehe ich aber dann doch anders:
Und damit man das nicht immer neu bewerten muss, macht es Sinn einfach immer eine korrekte Behandlung solcher potentiellen Probleme einzubauen. Denn sonst müsstest du ja bei jeder Änderung schauen, ob deine Prozedur nicht irgendwo aufgerufen wird, wo dann eine Behandlung nötig wird...
Ich finde den Ansatz sinnvoller, mich dabei auf die potenziell problematischen Stellen zu beschränken.
Entweder weiß ich von vorn herein, dass an bestimmten Stellen ein
Exception-Problem auftreten kann, das berücksichtigt werden muss oder ich merke es bei der Entwicklung bzw. in einer Testphase.
Wenn ich 1000 Stellen im Code mit Schutzblöcken absichere und nur eine ein wirkliches Risiko beinhaltet, dann finde ich den Aufwand einfach nicht zu rechtfertigen (zumal der Code auch schlechter wartbar wird).
Wenn das mögliche Problem in Deinem Beispiel nicht vermeidbar ist (z.B. Netzkabel gezogen) und Deine Exceptionbehandlung das Problem vollständig löst, so dass das Programm korrekt mit konsistenten Daten weiter arbeitet, dann ist die Lösung absolut sinnvoll.
Eine Datenbereinigung ist dort jedoch auch nicht skizziert, sondern nur ein Log und eine Infobox.
Wenn KEINE ausdrückliche Datenbereinigung erfolgt, dann muss vor einer Fortsetzung der Arbeit erst mal der Datenbestand gesichert/geprüft und der Fehler in der Software schnell bereinigt werden.
In dem Moment komme ich wieder zu dem Schluss, dass die finallys in dem Run völlig verzichtbar sind:
Dein Code wäre dann m.E. gleichwertig zu folgendem:
Delphi-Quellcode:
procedure Run;
begin
obj1 := TObject.Create;
Beep;
obj2 := TObject.Create;
obj3 := TObject.Create;
Beep;
obj3.Free;
obj2.Free;
obj1.Free;
end;
try
Run;
except
on E:
Exception do
begin
// VollständigeDatenprüfungUndBereinigung;
WriteLog(E);
UserDialog(Format('
Interner Fehler: %s - %s', [E.ClassName, E.
Message]));
end;
end;
Hier mal angenommen, der Fehler wäre nicht komplett ausgebügelt und der Datenbestand möglicherweise inkonsistent.
In beiden Fällen erhalten wir eine Info und einen Log aber wir wüssten nicht, was nun mit den Daten ist.
Der Fehler müsste bereinigt werden und das Programm ein Update erhalten.
Eine Weiterarbeit wäre dem Anwender nicht zu empfehlen, weil wir nichts über den Datenbestand sagen können.
Die verkürzte Run-Prozedur hätte keine wirklichen Nachteile gegenüber Deiner. Ok, da dümpeln noch 1 - 3 Speicherbereiche im Speicher rum, aber das Programm muss ja sowiso beendet werden.
Wenn da natürlich die VollständigeDatenprüfungUndBereinigung eingebaut wäre, dann wäre Deine Lösung perfekt und absolut richtig.
Fazit:
- Exceptionbehandlung mit Datenbereinigung -> Object.Free im Finally sinnvoll
- keine Exceptionbehandlung mit vollständiger Datenbereinigung -> Object.Free im Finally nicht hilfreich
Und vollständige Datenbereinigung bei möglichen Fehlern kann man nur bei erwarteten bzw. denkbaren Fehlern durchführen.