Was ist der Sinn von schließlich in einem Versuch fangen / außer schließlich Anweisung

73

Ich habe jahrelang Try-Catch / Except-finally-Varianten in vielen Sprachen verwendet. Heute hat mich jemand gefragt, worum es bei Final geht, und ich konnte nicht antworten.

Grundsätzlich, warum sollten Sie eine Anweisung endgültig einfügen, anstatt sie nur nach dem gesamten Try-Catch-Block einzufügen? Oder mit anderen Worten, gibt es einen Unterschied zwischen den folgenden Codeblöcken:

try{ //a}
catch {//b}
finally {//c}


try{//a}
catch{//b}
//c

EDIT:
PEOPLE, ich weiß, was endlich funktioniert, ich benutze es schon seit Ewigkeiten, aber meine Frage ist, dass das Einfügen //cim obigen Beispiel endgültig überflüssig erscheint, nicht wahr?

Ali
quelle

Antworten:

97

Der Zweck eines finallyBlocks besteht darin, sicherzustellen, dass Code unter drei Umständen ausgeführt wird, die mit "catch" -Blöcken allein nicht sehr sauber gehandhabt werden können:

  1. Wenn der Code innerhalb des tryBlocks über beendet wirdreturn
  2. Wenn Code innerhalb eines catchBlocks entweder die abgefangene Ausnahme erneut auslöst oder - versehentlich oder absichtlich - eine neue auslöst.
  3. Wenn der Code innerhalb des tryBlocks auf eine Ausnahme stößt, für die es keinen Haken gibt.

Man könnte den finallyCode vor jedem returnoder jedem Wurf kopieren und catchBlöcke in ihren eigenen Versuch / Fang einschließen, um die Möglichkeit einer versehentlichen Ausnahme zu berücksichtigen, aber es ist viel einfacher, auf all das zu verzichten und einfach einen finallyBlock zu verwenden.

Übrigens, eine Sache, die Sprachdesigner gerne einschließen würden, wäre ein exceptionArgument für den finallyBlock, um den Fall zu behandeln, in dem man nach einer Ausnahme bereinigen muss, aber dennoch möchte, dass er den Aufrufstapel durchdringt (z. B. könnte man den Code umbrechen ein Konstruktor in einem solchen Konstrukt und Disposedas im Aufbau befindliche Objekt, wenn der Konstruktor mit einer Ausnahme beendet werden soll).

Superkatze
quelle
17
Wir haben es versucht, Sie haben es verstanden, und wir haben endlich eine Antwort auf diese Frage!
Daniel Möller
2
Ich denke, der Schlüssel hier war, dass es auch ausgeführt wird, selbst wenn returndie try/catchAnweisung eine Anweisung enthält. Ohne dieses Verständnis finallyscheint eine Klausel ziemlich trivial zu sein, wenn man weiß, dass a catch allim Wesentlichen die try/catchAusführung des Codes nach dem Block unabhängig von einer Ausnahme ermöglicht. Vielen Dank, @supercat!
Wirbel
1
@Swivel: Sowohl die Rückgabe als auch die (normalerweise erneut) ausgelöste Ausnahme in einem Catch-Fall sind einfacher zu handhaben finallyals ohne.
Supercat
2

Schließlich wird der Block auch dann ausgeführt, wenn im try-Block eine Ausnahme ausgelöst wird. Wenn Sie beispielsweise zuvor einen Stream geöffnet haben, möchten Sie diesen Stream möglicherweise schließen, entweder wird eine Ausnahme ausgelöst oder nicht. Schließlich ist Block für ein solches Problem nützlich.

Ahmet B. Badın
quelle
1
Sie können den Stream aber auch nach dem Try-Catch schließen, ohne ihn endgültig zu blockieren.
Gereon99
1
Das beantwortet die Frage von OP nicht.
Dan Dascalescu
0

finallyist ein syntaktischer Zucker, um das DRY-Prinzip im try-catchMuster zuzulassen . Eine Ausnahme wird normalerweise ausgelöst, wenn der Bibliothekscode nicht genügend Informationen enthält, um einen bestimmten Status zu verarbeiten, und der Clientcode ihn lösen soll. Wenn Sie keine Trennung zwischen Bibliothek und Client-Code haben, können Sie alles mit ifstatt behandeln try.

Sehen wir uns eine Standardsituation ohne an finally:

void myFunction() {
  var r = allocateResources();
  r.doSomething();
  if(somethingBadHappens) {
    freeResources(r);
    throw new Exception(CODE42);
  }
  r.doSomethingMore();
  freeResources(r);
}

Im obigen Snippet wiederholen Sie freeResources(): Dies können mehrere Anweisungen sein, die Sie wiederholen müssen. Dieser Geruch und finallyBlock ist die Lösung für sauberen Code:

void myFunction() {
  var r = allocateResources();
  try {
    r.doSomething();
    if(somethingBadHappens) throw new Exception(CODE42);
    r.doSomethingMore();
  }
  finally {
    freeResources(r);
  }
  happyFunction();
}

Lassen Sie uns drei Abstraktionsebenen realisieren:

  • A1 ist der Bibliothekscode, der die allocateResources()Funktion bereitstellt
  • A2 ist unser Code myFunction, der A1 verbraucht
  • A3 ist ein Client-Code, der myFunctionim Try-Catch-Block verbraucht wird :
function A3code() {
  try {
    myFunction();
    doSomething();
  }
  catch(Exception e) {
    // no hanging resources here
    Console.WriteLine(e);
  }
}

Nun wollen wir sehen, was passieren kann:

  • Wenn allocateResources()wir A1 einwerfen, wissen wir nicht, wie wir in A2 damit umgehen sollen (A2-Code kann in einer konsolenfreien Umgebung ausgeführt werden), daher verschieben wir die Situation auf A3, ohne weiteren Code hinzuzufügen. Wenn hier eine Ausnahme ausgelöst wird, wird der finally-Block nicht ausgeführt , da an diesen finallygebunden ist, tryder nicht eingegeben wurde.
  • wenn somethingBadHappensin try - Block, die Stapel Abwicklungen bis A3 , wo die Situation gehandhabt wird , aber bevor es finallywird Block ausgeführt , so dass wir es nicht brauchen zu wiederholen , wenn keine Ausnahmen passieren.
  • bevor finallywir catchBlock hinzufügen und versuchen können, einige mögliche Ausnahmen von A1 zu lösen, die in aufrufenden r.doSomethingMethoden auftreten können. Normalerweise möchten wir Ausnahmen so schnell wie möglich behandeln, um den Client-Code (A3) für Client-Codierer komfortabler zu gestalten.
  • happyFunction()wird nur ausgeführt, wenn nichts myFunction()hineingeworfen wird (innerhalb oder außerhalb des tryBlocks).

Wie @supercat hervorhebt, wird der finallyBlock auch ausgeführt, wenn der tryBlock über return beendet wird. Ich schlage vor, Sie vermeiden diese schlechte Angewohnheit und haben nur eine Rückkehr in jeder Funktion (möglicherweise gibt es einige früh am Anfang der Funktionen). Die Gründe für Single-Return-Funktionen sind:

  1. Der Code ist besser lesbar: Sie sehen am Ende, WAS die Funktion zurückgibt. Bei mehreren Rückgaben müssen Sie alle Rückgabevorkommen finden, alle Wenns überprüfen, darüber nachdenken, wann die Wenns erfüllt sind, und erst dann wissen Sie, was die Funktion zurückgibt.
  2. Der Code kann von Compilern optimiert werden, siehe Kopierentscheidung .

Der Grund für mehrere Rückgaben besteht darin, viele verschachtelte Wenns zu vermeiden, aber es gibt andere Techniken, um dies zu lösen. Exceptions sind eine Ausnahme in dieser Regel.

Jan Turoň
quelle
-1

Finally Stellen Sie sicher, dass Ihr Code ausgeführt wird, auch wenn Sie eine Ausnahme erhalten.

Der finally-Block ist nützlich, um alle im try-Block zugewiesenen Ressourcen zu bereinigen und Code auszuführen, der ausgeführt werden muss, auch wenn eine Ausnahme vorliegt

http://msdn.microsoft.com/en-us/library/zwc8s4fz(v=vs.80).aspx

Massimiliano Peluso
quelle
4
aber was ist der Unterschied zwischen den beiden Codeblöcken, die ich erwähnt habe? in beiden Fällen //cwird ausgeführt, oder?
Ali
Bitte beachten Sie die EDITED Frage.
Ali
@Ali - Mit einem finally-Block können Sie alle zugewiesenen Ressourcen bereinigen und der excpetion ermöglichen, den Aufrufstapel zu sichern, wenn Sie die Ausnahme nicht lokal verarbeiten.
Dampsquid
6
in der zweiten Version würde // c NICHT ausgeführt, wenn der Code in // a 'return' aufgerufen würde, um die Funktion zu verlassen. stellt schließlich sicher, dass // c aufgerufen wird, auch wenn die Funktion vorzeitig beendet wird. ... schwehr.org/blog/archives/2012-03.html#e2012-03-01T13_11_15.txt
Barryhunter
5
Beantwortet die eigentliche Frage nicht.
Adaddinsane