it-swarm.com.de

Warum ist der Versuch {...} endlich {...} gut? versuche {...} schlecht zu fangen?

Ich habe Leute sagen sehen, dass es eine schlechte Form ist, catch ohne Argumente zu verwenden, besonders wenn dieser catch nichts bewirkt:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

Dies gilt jedoch als gute Form:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

Soweit ich das beurteilen kann, besteht der einzige Unterschied zwischen dem Einfügen von Bereinigungscode in einen finally-Block und dem Einfügen von Bereinigungscode nach den try..catch-Blöcken darin, dass Sie return-Anweisungen in Ihrem try-Block haben (in diesem Fall wird der Bereinigungscode in finally verwendet) laufen, aber Code nach dem try..catch wird nicht).

Ansonsten, was ist das Besondere an endlich?

193
mbeckish

Der große Unterschied ist, dass try...catch schluckt die Ausnahme und verbirgt die Tatsache, dass ein Fehler aufgetreten ist. try..finally führt Ihren Bereinigungscode aus und die Ausnahme wird fortgesetzt, um von etwas behandelt zu werden, das weiß, was damit zu tun ist.

349
Khoth

"Schließlich" ist eine Aussage von "Etwas, das Sie immer tun müssen, um sicherzustellen, dass der Programmstatus normal ist". Daher ist es immer eine gute Form, eine zu haben, wenn es eine Möglichkeit gibt, dass Ausnahmen den Programmstatus beeinträchtigen. Der Compiler ist auch sehr bemüht, sicherzustellen, dass Ihr finally-Code ausgeführt wird.

"Catch" ist eine Aussage von "Ich kann mich von dieser Ausnahme erholen". Sie sollten sich nur von Ausnahmen erholen, die Sie wirklich korrigieren können - fangen Sie ohne Argumente "Hey, ich kann mich von allem erholen!", Was fast immer falsch ist.

Wenn es wäre möglich, sich von jeder Ausnahme zu erholen, dann wäre es wirklich ein semantischer Streit darüber, wofür Sie Ihre Absicht erklären. Dies ist jedoch nicht der Fall, und mit ziemlicher Sicherheit sind Bilder über dem Ihren für bestimmte Ausnahmen besser gerüstet. Verwenden Sie als solches schließlich, um Ihren Bereinigungscode kostenlos auszuführen, und lassen Sie dennoch sachkundigere Behandler das Problem lösen.

59
Adam Wright

Denn wenn diese eine einzelne Zeile eine Ausnahme auslöst, würden Sie es nicht wissen.

Mit dem ersten Codeblock ist die Ausnahme einfach absorbiert, das Programm wird weiterhin ausgeführt, auch wenn der Status des Programms möglicherweise falsch ist.

Mit dem zweiten Block wird die Ausnahme ausgelöst und sprudelt , aber die reader.Close() ist immer noch garantiert zu rennen.

Wenn eine Ausnahme nicht erwartet wird, setzen Sie keinen try..catch-Block, nur damit, es wird später schwer zu debuggen sein, wenn das Programm in einen schlechten Zustand überging und Sie keine Ahnung haben, warum.

32
chakrit

Endlich wird ausgeführt, egal was passiert. Wenn Ihr try-Block erfolgreich war, wird er ausgeführt. Wenn Ihr try-Block fehlschlägt, wird er den catch-Block und dann den finally-Block ausführen.

Versuchen Sie außerdem, das folgende Konstrukt zu verwenden:

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

Da die using-Anweisung automatisch in try/finally eingeschlossen wird, wird der Stream automatisch geschlossen. (Sie müssen die using-Anweisung mit try/catch umgehen, wenn Sie die Ausnahme tatsächlich abfangen möchten.).

21
Mark Ingram

Die folgenden zwei Codeblöcke sind zwar äquivalent, aber nicht gleich.

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. 'finally' enthüllt die Absicht. Sie erklären dem Compiler und anderen Programmierern, dass dieser Code ausgeführt werden muss, egal was passiert.
  2. wenn Sie mehrere catch-Blöcke und Bereinigungscode haben, müssen Sie schließlich. Ohne letztendlich würden Sie Ihren Bereinigungscode in jedem Catch-Block duplizieren. (DRY-Prinzip)

endlich sind Blöcke etwas Besonderes. Die CLR erkennt und behandelt Code, der einen finally-Block enthält, getrennt von catch-Blöcken, und die CLR unternimmt große Anstrengungen, um sicherzustellen, dass ein finally-Block immer ausgeführt wird. Es ist nicht nur syntaktischer Zucker vom Compiler.

8
Robert Paulson

Ich stimme dem zu, was hier der Konsens zu sein scheint - ein leerer 'catch' ist schlecht, weil er jede Ausnahme maskiert, die im try-Block aufgetreten sein könnte.

Unter dem Gesichtspunkt der Lesbarkeit gehe ich davon aus, dass es beim Anzeigen eines 'try'-Blocks eine entsprechende' catch'-Anweisung gibt. Wenn Sie nur "try" verwenden, um sicherzustellen, dass die Ressourcendezuweisung im "finally" -Block aufgehoben wird, können Sie stattdessen die "using" -Anweisung in Betracht ziehen:

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

Sie können die Anweisung 'using' mit jedem Objekt verwenden, das IDisposable implementiert. Die dispose () -Methode des Objekts wird am Ende des Blocks automatisch aufgerufen.

5
Chris Lawlor

Der try..finally-Block löst weiterhin alle Ausnahmen aus, die ausgelöst werden. Alles, was finally tut, ist sicherzustellen, dass der Bereinigungscode ausgeführt wird, bevor die Ausnahme ausgelöst wird.

Der try..catch-Befehl mit einem leeren catch-Befehl verbraucht alle Ausnahmen vollständig und verbirgt die Tatsache, dass dies geschehen ist. Der Leser wird geschlossen, aber es kann nicht gesagt werden, ob das Richtige passiert ist. Was wäre, wenn Sie i in die Datei schreiben wollten? In diesem Fall schaffen Sie es nicht zu diesem Teil des Codes und myfile.txt ist leer. Gehen alle nachgelagerten Methoden ordnungsgemäß damit um? Wenn Sie die leere Datei sehen, können Sie dann richtig erraten, dass sie leer ist, weil eine Ausnahme ausgelöst wurde? Wirf lieber die Ausnahme und lass wissen, dass du etwas falsch machst.

Ein weiterer Grund ist, dass der Versuch, so etwas zu fangen, völlig falsch ist. Was Sie damit sagen, ist: "Egal, was passiert, ich kann damit umgehen." Was ist mit StackOverflowException, können Sie danach aufräumen? Was ist mit OutOfMemoryException? Im Allgemeinen sollten Sie nur Ausnahmen behandeln, die Sie erwarten und die Sie zu behandeln wissen.

3
OwenP

Verwenden Try..Catch..Finally, wenn Ihre Methode weiß, wie die Ausnahme lokal behandelt wird. Die Ausnahme tritt in "Try", "Handled in Catch" und anschließend in "finally" auf.

Falls Ihre Methode die Ausnahmebehandlung nicht versteht, aber nach dem Auftreten eine Bereinigung benötigt, verwenden Sie Try..Finally

Auf diese Weise wird die Ausnahme an die aufrufenden Methoden weitergegeben und behandelt, wenn die aufrufenden Methoden geeignete Catch-Anweisungen enthalten. Wenn die aktuelle Methode oder eine der aufrufenden Methoden keine Ausnahmebehandlungsroutinen enthält, stürzt die Anwendung ab.

Durch Try..Finally Es wird sichergestellt, dass die lokale Bereinigung durchgeführt wird, bevor die Ausnahme an die aufrufenden Methoden weitergegeben wird.

3
manjuv

Wenn Sie nicht wissen, welchen Ausnahmetyp Sie abfangen sollen oder was Sie damit tun sollen, ist es sinnlos, eine catch-Anweisung zu haben. Sie sollten es einfach einem übergeordneten Anrufer überlassen, der mehr Informationen über die Situation hat, um zu wissen, was zu tun ist.

Sie sollten immer noch eine finally-Anweisung haben, falls es eine Ausnahme gibt, damit Sie Ressourcen bereinigen können, bevor diese Ausnahme an den Aufrufer gesendet wird.

2
Mark Cidade

Aus Sicht der Lesbarkeit wird künftigen Codelesern expliziter gesagt, "dieses Zeug hier ist wichtig, es muss getan werden, egal was passiert." Das ist gut.

Außerdem haben leere catch-Anweisungen einen gewissen "Geruch". Dies könnte ein Zeichen dafür sein, dass Entwickler nicht über die verschiedenen Ausnahmen nachdenken, die auftreten können, und wie sie damit umgehen sollen.

2
Ryan

Schließlich ist optional - es gibt keinen Grund, einen "Schließlich" -Block zu haben, wenn keine zu bereinigenden Ressourcen vorhanden sind.

2
Guy Starbuck

Entnommen aus: hier

Das Auslösen und Abfangen von Ausnahmen sollte nicht routinemäßig als Teil der erfolgreichen Ausführung einer Methode erfolgen. Beim Entwickeln von Klassenbibliotheken muss dem Clientcode die Möglichkeit gegeben werden, auf einen Fehlerzustand zu testen, bevor eine Operation ausgeführt wird, die zur Auslösung einer Ausnahme führen kann. Beispielsweise stellt System.IO.FileStream eine CanRead-Eigenschaft bereit, die vor dem Aufrufen der Read-Methode überprüft werden kann, um zu verhindern, dass eine potenzielle Ausnahme ausgelöst wird, wie im folgenden Codeausschnitt dargestellt:

Dim str As Stream = GetStream () If (str.CanRead) Then 'Code zum Lesen des Streams End If

Die Entscheidung, ob der Status eines Objekts vor dem Aufrufen einer bestimmten Methode, die möglicherweise eine Ausnahme auslöst, überprüft werden soll, hängt vom erwarteten Status des Objekts ab. Wenn ein FileStream-Objekt mit einem vorhandenen Dateipfad und einem Konstruktor erstellt wird, der eine Datei im Lesemodus zurückgeben soll, muss die CanRead-Eigenschaft nicht überprüft werden. Die Unfähigkeit, den FileStream zu lesen, würde das erwartete Verhalten der ausgeführten Methodenaufrufe verletzen, und es sollte eine Ausnahme ausgelöst werden. Im Gegensatz dazu ist es ratsam, die CanRead-Eigenschaft zu überprüfen, bevor Sie versuchen, Daten zu lesen, wenn eine Methode so dokumentiert ist, dass sie einen FileStream-Verweis zurückgibt, der möglicherweise lesbar oder nicht lesbar ist.

Um die Auswirkungen auf die Leistung zu veranschaulichen, die die Verwendung einer Codierungstechnik mit der Aufforderung "Bis zur Ausnahme ausführen" verursachen kann, wird die Leistung einer Umwandlung, die eine InvalidCastException auslöst, wenn die Umwandlung fehlschlägt, mit dem Operator C # as verglichen, der bei fehlgeschlagener Umwandlung Nullen zurückgibt. Die Leistung der beiden Techniken ist identisch für den Fall, dass die Besetzung gültig ist (siehe Test 8.05), aber für den Fall, dass die Besetzung ungültig ist und die Verwendung einer Besetzung eine Ausnahme verursacht, ist die Verwendung einer Besetzung 600-mal langsamer als die Verwendung von als Bediener (siehe Test 8.06). Die Auswirkungen der Technik zum Auslösen von Ausnahmen auf die Leistung umfassen die Kosten für das Zuweisen, Auslösen und Auffangen der Ausnahme sowie die Kosten für die anschließende Speicherbereinigung des Ausnahmeobjekts. Dies bedeutet, dass die unmittelbaren Auswirkungen des Auslösens einer Ausnahme nicht so hoch sind. Wenn mehr Ausnahmen ausgelöst werden, wird die häufige Speicherbereinigung zu einem Problem. Die häufige Verwendung einer Codierungstechnik, die Ausnahmen auslöst, hat daher ähnliche Auswirkungen wie Test 8.05.

2

Wenn Sie C # für Programmierer lesen, werden Sie verstehen, dass der finally-Block so konzipiert wurde, dass eine Anwendung optimiert und ein Speicherverlust verhindert wird.

Die CLR beseitigt Lecks nicht vollständig ... Speicherlecks können auftreten, wenn das Programm versehentlich Verweise auf unerwünschte Objekte speichert

Wenn Sie beispielsweise eine Datei- oder Datenbankverbindung öffnen, weist Ihr Computer dieser Transaktion Speicher zu, und dieser Speicher wird nur beibehalten, wenn der Befehl verfügt oder geschlossen ausgeführt wurde. Wenn jedoch während der Transaktion ein Fehler aufgetreten ist, wird der Vorgang nicht abgebrochen, es sei denn, er befand sich innerhalb des try.. finally.. Block.

catch unterscheidet sich von finally in dem Sinne, dass catch so konzipiert ist, dass Sie den Fehler selbst handhaben/verwalten oder interpretieren können. Stellen Sie sich das als Person vor, die Ihnen sagt: "Hey, ich habe ein paar Bösewichte gefangen, was soll ich ihnen antun?" while finally wurde entwickelt, um sicherzustellen, dass Ihre Ressourcen ordnungsgemäß platziert wurden. Denken Sie an jemanden, der sicherstellt, dass Ihr Eigentum noch sicher ist, ob es Bösewichte gibt oder nicht.

Und Sie sollten diese beiden für immer zusammenarbeiten lassen.

beispielsweise:

try
{
  StreamReader reader=new  StreamReader("myfile.txt");
  //do other stuff
}
catch(Exception ex){
 // Create log, or show notification
 generic.Createlog("Error", ex.message);
}
finally   // Will execute despite any exception
{
  reader.Close();
}
2
dr.Crow

Es ist eine schlechte Praxis, eine catch-Klausel hinzuzufügen, um die Ausnahme erneut auszulösen.

2

Mit finally können Sie Ressourcen bereinigen, auch wenn Ihre catch-Anweisung die Ausnahme bis zum aufrufenden Programm auslöst. Bei Ihrem Beispiel mit der leeren catch-Anweisung gibt es kaum einen Unterschied. Wenn Sie jedoch in Ihrem Fang etwas verarbeiten und den Fehler auslösen oder gar keinen Fang haben, wird der letztendlich immer noch ausgeführt.

1
Kibbee

Zum einen ist es eine schlechte Praxis, Ausnahmen abzufangen, mit denen Sie sich nicht abfinden müssen. Lesen Sie Kapitel 5 zur .Net-Leistung von Verbessern der Leistung und Skalierbarkeit von .NET-Anwendungen . Nebenbei bemerkt, Sie sollten wahrscheinlich den Stream innerhalb des try-Blocks laden. Auf diese Weise können Sie die entsprechende Ausnahme abfangen, wenn sie fehlschlägt. Das Erstellen des Streams außerhalb des try-Blocks hat keinen Zweck.

1
Factor Mystic

versuchen {…} fangen {} ist nicht immer schlecht. Es ist kein gängiges Muster, aber ich benutze es eher, wenn ich Ressourcen herunterfahren muss, egal was passiert, wie das Schließen eines (möglicherweise) offenen Sockets am Ende eines Threads.

0
Martin Liesén

Ausnahmen sind wahrscheinlich aus vielen Gründen sehr langsam auszuführen. Sie können Ihre Ausführungszeiten leicht verkrüppeln, wenn dies häufig vorkommt.

0
lotsoffreetime

Das Problem mit try/catch-Blöcken, die alle Ausnahmen abfangen, besteht darin, dass sich Ihr Programm jetzt in einem unbestimmten Zustand befindet, wenn eine unbekannte Ausnahme auftritt. Dies verstößt vollständig gegen die Fail-Fast-Regel. Sie möchten nicht, dass Ihr Programm fortgesetzt wird, wenn eine Ausnahme auftritt. Der obige try/catch-Befehl würde sogar OutOfMemoryExceptions abfangen, aber das ist definitiv ein Zustand, in dem Ihr Programm nicht ausgeführt wird.

Mit Try/finally-Blöcken können Sie Bereinigungscode ausführen, während der Fehler weiterhin schnell auftritt. In den meisten Fällen möchten Sie nur alle Ausnahmen auf globaler Ebene abfangen, damit Sie sie protokollieren und dann beenden können.

0
David Mohundro

Der effektive Unterschied zwischen Ihren Beispielen ist vernachlässigbar, solange keine Ausnahmen geworfen werden.

Wenn jedoch in der 'try'-Klausel eine Ausnahme ausgelöst wird, wird sie im ersten Beispiel vollständig verschluckt. Im zweiten Beispiel wird die Ausnahme für den nächsten Schritt des Aufrufstapels ausgelöst, sodass der Unterschied in den angegebenen Beispielen darin besteht, dass eine Ausnahme (erstes Beispiel) vollständig verdeckt und die andere (zweites Beispiel) Ausnahmeinformationen für eine mögliche spätere Behandlung aufbewahrt den Inhalt in der 'finally'-Klausel noch ausführen.

Wenn Sie beispielsweise Code in die 'catch'-Klausel des ersten Beispiels einfügen, in dem eine Ausnahme ausgelöst wurde (entweder die ursprünglich ausgelöste oder eine neue), wird der Bereinigungscode des Lesers niemals ausgeführt. Schließlich führt nabhängig aus, was in der 'catch'-Klausel passiert.

Der Hauptunterschied zwischen 'catch' und 'finally' besteht also darin, dass der Inhalt des 'finally'-Blocks (mit wenigen seltenen Ausnahmen) als garantiert ausgeführt betrachtet werden kann, auch angesichts eines Unerwartete Ausnahme, während Code, der auf eine 'catch'-Klausel folgt (jedoch außerhalb einer' finally'-Klausel), keine solche Garantie bietet.

Im Übrigen implementieren Stream und StreamReader beide IDisposable und können in einen using-Block eingeschlossen werden. 'Using'-Blöcke sind das semantische Äquivalent von try/finally (kein' catch '), daher könnte Ihr Beispiel enger ausgedrückt werden als:

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

... der die StreamReader-Instanz schließt und löscht, wenn sie den Gültigkeitsbereich verlässt. Hoffe das hilft.

0
Jared