2010-05-26 9 views
225

Wenn ein finally-Block eine Ausnahme auslöst, was passiert genau ?Was passiert, wenn ein finally-Block eine Ausnahme auslöst?

Konkret, was passiert, wenn die Ausnahme mitten in einem finally-Block ausgelöst wird. Werden die restlichen Anweisungen (after) in diesem Block aufgerufen?

Ich bin mir bewusst, dass Ausnahmen nach oben propagieren.

+8

Warum es gerade nicht versuchen? Aber bei solchen Dingen ist die eine, die ich am meisten mag, die Rückkehr vor der endgültigen und dann die Rückkehr aus dem letzten Block. :) – ANeves

+8

Alle Anweisungen in einem finally Block müssen ausgeführt werden. Es kann keine Rückkehr haben. http://msdn.microsoft.com/en-us/library/0hbbzekw(VS.80).aspx –

Antwort

348

Wenn ein finally Block eine Ausnahme auslöst, was passiert genau passiert?

Diese Ausnahme wird nach oben und nach oben weitergegeben und kann (kann) auf einer höheren Ebene behandelt werden.

Ihr endgültiger Block wird nicht abgeschlossen werden über den Punkt hinaus, wo die Ausnahme ausgelöst wird.

Wenn der finally-Block während der Behandlung einer früheren Ausnahme ausgeführt wurde, ist diese erste Ausnahme verloren.

C# 4 Sprachspezifikation § 8.9.5: Wenn der finally-Block eine weitere Ausnahme auslöst, wird die Verarbeitung der aktuellen Ausnahme beendet.

+33

+1: Die einzige Antwort, die ** vollständig ** die Frage beantwortet –

+3

Wenn eine Ausnahme aus dem 'try' Block geworfen wird, wird es gegessen. –

+8

Wenn es sich nicht um eine 'ThreadAbortException' handelt, wird der gesamte finally-Block zuerst beendet, da es sich um einen kritischen Abschnitt handelt. –

5

Die Ausnahme wird propagiert.

+1

@bitbonk: von innen nach außen, wie üblich. – Piskvor

0

Es wirft eine Ausnahme;) Sie können diese Ausnahme in einer anderen catch-Klausel fangen.

1
public void MyMethod() 
{ 
    try 
    { 
    } 
    catch{} 
    finally 
    { 
     CodeA 
    } 
    CodeB 
} 

Die Art und Weise der von CodeA und CodeB geworfen Ausnahmen behandelt werden, ist das gleiche.

Eine ausgelöste Ausnahme in einem finally Block hat nichts Besonderes, sie als Wurf Ausnahme von Code B.

+0

Könnten Sie näher ausführen? Was meinst du mit den Ausnahmen sind die gleichen? –

+1

sorry siehe meine EDIT –

90

Für Fragen wie diese behandeln ich in der Regel eine leere -Konsolenanwendungsprojekt in Visual Studio öffnen und eine kleine Probe schreiben Programm:

using System; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      try 
      { 
       throw new Exception("exception thrown from try block"); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine("Inner catch block handling {0}.", ex.Message); 
       throw; 
      } 
      finally 
      { 
       Console.WriteLine("Inner finally block"); 
       throw new Exception("exception thrown from finally block"); 
       Console.WriteLine("This line is never reached"); 
      } 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Outer catch block handling {0}.", ex.Message); 
     } 
     finally 
     { 
      Console.WriteLine("Outer finally block"); 
     } 
    } 
} 

Wenn Sie das Programm ausführen finden Sie die genaue Reihenfolge, in der catch und finally Blöcke ausgeführt sehen. Bitte beachten Sie, dass Code in dem finally-Block, nachdem die Ausnahme ausgelöst wird, wird (in der Tat, in diesem Beispielprogramm Visual Studio werden Sie sogar warnen, dass es nicht erreichbaren Code erkannt hat) nicht ausgeführt werden:

 
Inner catch block handling exception thrown from try block. 
Inner finally block 
Outer catch block handling exception thrown from finally block. 
Outer finally block 

Zusätzliche Bemerkung

Als Michael Damatov wies darauf hin, eine Ausnahme von dem try Block wird „gefressen“ werden, wenn man damit umgeht nicht in einem (inneren) catch Block. Im obigen Beispiel erscheint die erneut geworfene Ausnahme nicht im äußeren catch-Block. Um das zu erreichen noch deutlicher Blick auf die folgenden leicht modifizierten Beispiel:

using System; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      try 
      { 
       throw new Exception("exception thrown from try block"); 
      } 
      finally 
      { 
       Console.WriteLine("Inner finally block"); 
       throw new Exception("exception thrown from finally block"); 
       Console.WriteLine("This line is never reached"); 
      } 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Outer catch block handling {0}.", ex.Message); 
     } 
     finally 
     { 
      Console.WriteLine("Outer finally block"); 
     } 
    } 
} 

Wie Sie aus dem Ausgang der innere Ausnahme sehen „verloren“ (dignoriert):

 
Inner finally block 
Outer catch block handling exception thrown from finally block. 
Outer finally block 
+2

Weil Sie die Ausnahme in Ihrem inneren Fang, wird 'Inner finally Block' in diesem Beispiel nie erreicht werden –

+3

@Theofanis Pantelides: Nein, ein 'endlich' Block wird (fast) immer ausgeführt werden, dies gilt auch in diesem Fall für den inneren finally-Block (probiere einfach das Beispielprogramm selbst aus) (Ein finally-Block wird nicht ausgeführt im Falle einer nicht behebbaren Ausnahme, zB eine 'EngineExecutionException'), aber in diesem Fall wird dein Programm sofort beendet –

+8

+1: Eine nützlichere Antwort als nur die Spezifikation zu zitieren –

9

Wenn eine Ausnahme gibt es anhängig ist (wenn der try Block ein finally aber keine catch) hat, ersetzt die neue Ausnahme, dass ein.

Wenn keine Ausnahme aussteht, wird eine Ausnahme außerhalb des Blocks finally ausgelöst.

+0

Eine Ausnahme kann auch ausstehen, wenn es * einen passenden 'catch'-Block gibt, der eine Ausnahme (neu) auslöst. – stakx

1

Wenn eine Ausnahme ausgelöst wird, während eine andere Ausnahme aktiv ist, wird die erste Ausnahme durch die zweite (spätere) Ausnahme ersetzt.

Hier ist ein Code, der, was passiert, zeigt:

public static void Main(string[] args) 
    { 
     try 
     { 
      try 
      { 
       throw new Exception("first exception"); 
      } 
      finally 
      { 
       //try 
       { 
        throw new Exception("second exception"); 
       } 
       //catch (Exception) 
       { 
        //throw; 
       } 
      } 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e); 
     } 
    } 
  • Führen Sie den Code, und Sie werden „zweite Ausnahme“
  • Kommentar- der try und catch-Anweisungen und Sie werden sehen „erste Ausnahme“ sehen
  • Auch den Wurf auskommentieren; Anweisung und Sie werden wieder "zweite Ausnahme" sehen.
+0

Es ist erwähnenswert, dass es möglich ist, eine "schwere" Ausnahme zu bereinigen, die nur außerhalb eines bestimmten Codeblocks abgefangen wird, um eine Ausnahme zu erzeugen, die darin abgefangen und behandelt wird. Mit Ausnahmefiltern (verfügbar in vb.net, aber nicht C#) ist es möglich, diese Bedingung zu erkennen. Es gibt nicht viel, was Code tun kann, um es zu "handhaben", aber wenn man irgendeine Art von Logging-Framework verwendet, ist es fast sicher eine Protokollierung wert. Der C++ - Ansatz, dass Ausnahmen, die bei der Bereinigung auftreten, eine Systemschmelze auslösen, ist hässlich, aber Ausnahmen verschwinden zu lassen, ist IMHO scheußlich. – supercat

1

Vor einigen Monaten i konfrontiert auch so etwas,

private void RaiseException(String errorMessage) 
    { 
     throw new Exception(errorMessage); 
    } 

    private void DoTaskForFinally() 
    { 
     RaiseException("Error for finally"); 
    } 

    private void DoTaskForCatch() 
    { 
     RaiseException("Error for catch"); 
    } 

    private void DoTaskForTry() 
    { 
     RaiseException("Error for try"); 
    } 


     try 
     { 
      /*lacks the exception*/ 
      DoTaskForTry(); 
     } 
     catch (Exception exception) 
     { 
      /*lacks the exception*/ 
      DoTaskForCatch(); 
     } 
     finally 
     { 
      /*the result exception*/ 
      DoTaskForFinally(); 
     } 

ein solches Problem zu lösen, i wie

class ProcessHandler : Exception 
{ 
    private enum ProcessType 
    { 
     Try, 
     Catch, 
     Finally, 
    } 

    private Boolean _hasException; 
    private Boolean _hasTryException; 
    private Boolean _hasCatchException; 
    private Boolean _hasFinnallyException; 

    public Boolean HasException { get { return _hasException; } } 
    public Boolean HasTryException { get { return _hasTryException; } } 
    public Boolean HasCatchException { get { return _hasCatchException; } } 
    public Boolean HasFinnallyException { get { return _hasFinnallyException; } } 
    public Dictionary<String, Exception> Exceptions { get; private set; } 

    public readonly Action TryAction; 
    public readonly Action CatchAction; 
    public readonly Action FinallyAction; 

    public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null) 
    { 

     TryAction = tryAction; 
     CatchAction = catchAction; 
     FinallyAction = finallyAction; 

     _hasException = false; 
     _hasTryException = false; 
     _hasCatchException = false; 
     _hasFinnallyException = false; 
     Exceptions = new Dictionary<string, Exception>(); 
    } 


    private void Invoke(Action action, ref Boolean isError, ProcessType processType) 
    { 
     try 
     { 
      action.Invoke(); 
     } 
     catch (Exception exception) 
     { 
      _hasException = true; 
      isError = true; 
      Exceptions.Add(processType.ToString(), exception); 
     } 
    } 

    private void InvokeTryAction() 
    { 
     if (TryAction == null) 
     { 
      return; 
     } 
     Invoke(TryAction, ref _hasTryException, ProcessType.Try); 
    } 

    private void InvokeCatchAction() 
    { 
     if (CatchAction == null) 
     { 
      return; 
     } 
     Invoke(TryAction, ref _hasCatchException, ProcessType.Catch); 
    } 

    private void InvokeFinallyAction() 
    { 
     if (FinallyAction == null) 
     { 
      return; 
     } 
     Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally); 
    } 

    public void InvokeActions() 
    { 
     InvokeTryAction(); 
     if (HasTryException) 
     { 
      InvokeCatchAction(); 
     } 
     InvokeFinallyAction(); 

     if (HasException) 
     { 
      throw this; 
     } 
    } 
} 

und verwendet wie dieses

try 
{ 
    ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally); 
    handler.InvokeActions(); 
} 
catch (Exception exception) 
{ 
    var processError = exception as ProcessHandler; 
    /*this exception contains all exceptions*/ 
    throw new Exception("Error to Process Actions", exception); 
} 
eine Utility-Klasse gemacht

aber wenn Sie Parameter und Rückgabetypen verwenden möchten, ist das ein andere Geschichte

2

Ich musste dies für das Abfangen eines Fehlers versuchen, einen Strom zu schließen, der wegen einer Ausnahme nie geöffnet wurde.

errorMessage = string.Empty; 

try 
{ 
    byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent); 

    webRequest = WebRequest.Create(url); 
    webRequest.Method = "POST"; 
    webRequest.ContentType = "text/xml;charset=utf-8"; 
    webRequest.ContentLength = requestBytes.Length; 

    //send the request 
    using (var sw = webRequest.GetRequestStream()) 
    { 
     sw.Write(requestBytes, 0, requestBytes.Length); 
    } 

    //get the response 
    webResponse = webRequest.GetResponse(); 
    using (var sr = new StreamReader(webResponse.GetResponseStream())) 
    { 
     returnVal = sr.ReadToEnd(); 
     sr.Close(); 
    } 
} 
catch (Exception ex) 
{ 
    errorMessage = ex.ToString(); 
} 
finally 
{ 
    try 
    { 
     if (webRequest.GetRequestStream() != null) 
      webRequest.GetRequestStream().Close(); 
     if (webResponse.GetResponseStream() != null) 
      webResponse.GetResponseStream().Close(); 
    } 
    catch (Exception exw) 
    { 
     errorMessage = exw.ToString(); 
    } 
} 

wenn die webRequest erstellt wurde, aber ein Verbindungsfehler geschah während der

using (var sw = webRequest.GetRequestStream()) 

dann würde das schließlich eine Ausnahme fangen versuchen, Verbindungen zu schließen es gedacht, offen war, weil die webRequest erstellt worden war.

Wenn die endlich einen Try-Catch innen didnt haben, würde dieser Code eine nicht behandelte Ausnahme verursachen, während die webRequest

if (webRequest.GetRequestStream() != null) 

von dort verlassen Sie den Code aufräumen würde, ohne den Fehler richtig Handhabung, was geschehen und deshalb verursacht Probleme für die aufrufende Methode.

Hoffnung dies als Beispiel

2

Quick (und ziemlich offensichtlich) hilft Snippet "ursprüngliche Ausnahme" (geworfen in try Block) und Opfer "schließlich Ausnahme" (geworfen in finally Block), falls Original zu speichern Sie ist wichtiger:

try 
{ 
    throw new Exception("Original Exception"); 
} 
finally 
{ 
    try 
    { 
     throw new Exception("Finally Exception"); 
    } 
    catch 
    { } 
} 

Wenn obige Code ausgeführt wird, "Original Exception" breitet sich auf den Call-Stack und "Schließlich Exception" verloren.

1

Die Ausnahme wird weitergegeben und sollte auf einer höheren Ebene behandelt werden. Wenn die Ausnahme nicht auf der höheren Ebene behandelt wird, stürzt die Anwendung ab. Die Ausführung des "finally" -Blocks stoppt an dem Punkt, an dem die Ausnahme ausgelöst wird.

Unabhängig davon, ob es eine Ausnahme gibt oder nicht, wird "finally" Block ausgeführt.

  1. Wenn die „endlich“ Block nachdem eine Ausnahme im try-Block aufgetreten ist ausgeführt wird,

  2. und wenn diese Ausnahme nicht behandelt wird

  3. und wenn der finally-Block ein wirft exception

Dann ist die ursprüngliche Ausnahme, die in dem Versuch-Block aufgetreten ist, verloren.

public class Exception 
{ 
    public static void Main() 
    { 
     try 
     { 
      SomeMethod(); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
     } 
    } 

    public static void SomeMethod() 
    { 
     try 
     { 
      // This exception will be lost 
      throw new Exception("Exception in try block"); 
     } 
     finally 
     { 
      throw new Exception("Exception in finally block"); 
     } 
    } 
} 

Great article for Details

Verwandte Themen