Wann wird es endlich ausgeführt, wenn Sie eine Ausnahme vom catch-Block auslösen?


134
try {
   // Do stuff
}
catch (Exception e) {
   throw;
}
finally {
   // Clean up
}

Wann wird im obigen Block der finally-Block aufgerufen? Vor dem Werfen von e oder wird endlich gerufen und dann fangen?


14
ps du solltest nicht "e werfen"; weil dies den Stack-Trace der ursprünglichen Ausnahme durcheinander bringt. Sie sollten nur "werfen;". Oder erstellen Sie eine neue Ausnahme und setzen Sie die InnerException auf "e", bevor Sie sie auslösen.
Erv Walter

24
schließlich wäre eine ziemlich schlechte Wahl des Keyword sein , wenn es nicht ausgeführt werden zuletzt , würden Sie nicht sagen?
Eric Lippert

@ErvWalter ist das noch wahr? Ich teste es in VS2017 in beide Richtungen und es scheint genau das gleiche zu sein. Könnten Sie weitere Informationen oder eine Referenz angeben? Vielen Dank
Jeff Puckett

nur Namensvorschlag verwenden Ausnahme Ausnahme - reservieren Sie e für Veranstaltungen / Delegierte
Herr R

Antworten:


137

Es würde aufgerufen, nachdem e erneut geworfen wurde (dh nachdem der catch-Block ausgeführt wurde).

7 Jahre später bearbeiten - Ein wichtiger Hinweis ist, dass eder finallyBlock möglicherweise überhaupt nicht ausgeführt wird, wenn er nicht von einem Try / Catch-Block weiter oben im Aufrufstapel abgefangen oder von einem globalen Ausnahmebehandler behandelt wird .


18
und niemals, wenn Sie Envrionment.FailFast ()
Johannes Rudolph

16
Nach dem Versuch Antwort des Code in Brandons, sehe ich , dass das finallynicht , wenn die Ausnahme im vorhergehenden geworfen ausgeführt wird catchnie ist gefangen in einem äußeren try- catchBlock!
Andrew

3
Vielen Dank, dass Sie Ihre (akzeptierte) Antwort so bearbeitet haben, dass sie die neuen Informationen enthält.
Gordon Bean

3
Sie (Microsoft) sprechen auf der neuen Dokumentationsseite darüber: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… : "Innerhalb einer behandelten Ausnahme wird die Ausführung des zugehörigen finally-Blocks garantiert Wenn die Ausnahme nicht behandelt wird, hängt die Ausführung des finally-Blocks davon ab, wie die Ausnahme-Abwicklungsoperation ausgelöst wird. Dies hängt wiederum davon ab, wie Ihr Computer eingerichtet ist. "
DotNetSparky

1
Beachten Sie, dass "von einem Try / Catch-Block weiter oben im Aufrufstapel abgefangen" Framework-Handler wie die in ASP.NET oder einem Testläufer enthält. Ein besserer Ausdruck könnte sein: "Wenn Ihr Programm nach dem catch-Block weiter ausgeführt wird, wird der finally-Block ausgeführt."
ArrowCase

90

Warum probieren Sie es nicht aus:

outer try
inner try
inner catch
inner finally
outer catch
outer finally

mit Code (formatiert für vertikalen Raum):

static void Main() {
    try {
        Console.WriteLine("outer try");
        DoIt();
    } catch {
        Console.WriteLine("outer catch");
        // swallow
    } finally {
        Console.WriteLine("outer finally");
    }
}
static void DoIt() {
    try {
        Console.WriteLine("inner try");
        int i = 0;
        Console.WriteLine(12 / i); // oops
    } catch (Exception e) {
        Console.WriteLine("inner catch");
        throw e; // or "throw", or "throw anything"
    } finally {
        Console.WriteLine("inner finally");
    }
}

5
+1, für etwas so Einfaches solltest du es wirklich einfach so versuchen, wie Marc es getan hat. GJ illustriert es mit verschachteltem Versuch / Fang / endlich :)
Allen Rice

1
@AllenRice warum es versuchen, wenn Marc es bereits hat und ich einfach googeln kann, um Marc's Antwort zu finden? Oder versuchen Sie es besser selbst, erstellen Sie eine SO-Frage und beantworten Sie sie selbst zum Nutzen anderer.
Joshden

8
Bitte beachten Sie, dass, wenn Sie die Ausnahme im äußeren Fang nicht abfangen, der innere schließlich NIE ausgeführt wird !! In diesem Fall ist die Ausgabeouter try inner try inner catch Unhandled Exception: System.DivideByZeroException...
Andrew

1
@ Andrew du hast recht. Die Erklärung finden Sie hier MSDN Magazine 2008 September: Unhandled Exception Processing in der CLR (um chm zu öffnen, muss es entsperrt werden: File Properties -> General -> Unlock). Wenn Sie den äußeren catch-Block durch "catch (ArgumentException)" ersetzen, wird kein endgültiger Block ebenfalls fortgesetzt, da CLR keinen "Ausnahmebehandler" finden kann, der der Behandlung der Ausnahme "DivideByZeroException" zugestimmt hat.
Vladimir

35

Nachdem Sie alle Antworten hier gelesen haben, sieht es so aus, als ob die endgültige Antwort davon abhängt :

  • Wenn Sie eine Ausnahme innerhalb des catch-Blocks erneut auslösen und diese Ausnahme innerhalb eines anderen catch-Blocks abgefangen wird, wird alles gemäß der Dokumentation ausgeführt.

  • Wenn die erneut ausgelöste Ausnahme jedoch nicht behandelt wird, wird sie schließlich nie ausgeführt.

Ich habe dieses Codebeispiel in VS2010 mit C # 4.0 getestet

static void Main()
    {
        Console.WriteLine("Example 1: re-throw inside of another try block:");

        try
        {
            Console.WriteLine("--outer try");
            try
            {
                Console.WriteLine("----inner try");
                throw new Exception();
            }
            catch
            {
                Console.WriteLine("----inner catch");
                throw;
            }
            finally
            {
                Console.WriteLine("----inner finally");
            }
        }
        catch
        {
            Console.WriteLine("--outer catch");
            // swallow
        }
        finally
        {
            Console.WriteLine("--outer finally");
        }
        Console.WriteLine("Huzzah!");

        Console.WriteLine();
        Console.WriteLine("Example 2: re-throw outside of another try block:");
        try
        {
            Console.WriteLine("--try");
            throw new Exception();
        }
        catch
        {
            Console.WriteLine("--catch");
            throw;
        }
        finally
        {
            Console.WriteLine("--finally");
        }

        Console.ReadLine();
    }

Hier ist die Ausgabe:

Beispiel 1: Werfen Sie erneut in einen anderen Versuchsblock:
--outer try
---- inner try
---- inner catch
---- inner finally -
outer catch
--outer finally
Huzzah!

Beispiel 2: Werfen Sie erneut außerhalb eines anderen Versuchsblocks:
--try
--catch

Nicht behandelte Ausnahme: System.Exception: Ausnahme vom Typ 'System.Exception' wurde ausgelöst.
unter ConsoleApplication1.Program.Main () in C: \ local source \ ConsoleApplication1 \ Program.cs: Zeile 53


3
Toller Fang, das war mir nicht bewusst!
Andrew

1
Beachten Sie, dass der letzte möglicherweise ausgeführt wird, je nachdem, was Sie auswählen: stackoverflow.com/a/46267841/480982
Thomas Weller

1
Interessant ... Unter .NET Core 2.0 wird der letzte Teil nach der nicht behandelten Ausnahme ausgeführt.
Mahdi Ghiasi

Interessanterweise habe ich gerade einen Test sowohl in .NET Core 2.0 als auch in .NET Framework 4.6.1 durchgeführt und beide führen die endgültig nach der nicht behandelten Ausnahme aus. Hat sich dieses Verhalten geändert?
Cameron Bielstein

24

Ihr Beispiel würde sich mit diesem Code identisch verhalten:

try {
    try {
        // Do stuff
    } catch(Exception e) {
        throw e;
    }
} finally {
    // Clean up
}

Nebenbei bemerkt, wenn Sie wirklich meinen throw e;(dh dieselbe Ausnahme auslösen, die Sie gerade abgefangen haben), ist es viel besser, dies einfach zu tun throw;, da dadurch die ursprüngliche Stapelverfolgung beibehalten wird, anstatt eine neue zu erstellen.


Ich denke nicht, dass das richtig ist. Schließlich sollte innerhalb des äußeren Versuchsblocks nicht außerhalb sein
Matthew Pigram

@ MatthewPigram: Was meinst du? Der finallyBlock wird tatsächlich nach dem catchBlock ausgeführt (selbst wenn der catch-Block die Ausnahme erneut auslöst), was mein Snippet zu veranschaulichen versucht.
Daniel Pryden

Nach meiner Interpretation seines Beispiels versucht er, einen Try-Catch schließlich innerhalb eines anderen Try-Blocks durchzuführen. KEIN Versuchsfang innerhalb eines Versuchsfangs
Matthew Pigram

1
@MatthewPigram: Meine Antwort enthält überhaupt kein "try-catch-finally" -Konstrukt. Es hat ein "try-finally" und innerhalb des tryBlocks davon hat meine Antwort einen "try-catch". Ich versuche, das Verhalten des dreiteiligen Konstrukts anhand von zwei zweiteiligen Konstrukten zu erklären. Ich sehe kein Anzeichen für einen zweiten tryBlock in der ursprünglichen Frage, daher verstehe ich nicht, woher Sie das bekommen.
Daniel Pryden

12

Wenn es innerhalb eines Catch-Handler-Blocks eine nicht behandelte Ausnahme gibt, wird der finally-Block genau null Mal aufgerufen

  static void Main(string[] args)
  {
     try
     {
        Console.WriteLine("in the try");
        int d = 0;
        int k = 0 / d;
     }
     catch (Exception e)
     {
        Console.WriteLine("in the catch");
        throw;
     }
     finally
     {
        Console.WriteLine("In the finally");
     }
  }

Ausgabe:

C: \ Benutzer \ Administrator \ Dokumente \ TestExceptionNesting \ bin \ Release> TestExceptionNesting.exe

im Versuch

im Fang

Nicht behandelte Ausnahme: System.DivideByZeroException: Es wurde versucht, durch Null zu teilen. bei TestExceptionNesting.Program.Main (String [] args) in C: \ Benutzer \ Administrator \ Dokumente \ TestExceptionNesting \ TestExceptionNesting.cs: Zeile 22

C: \ Benutzer \ Administrator \ Dokumente \ TestExceptionNesting \ bin \ release>

Diese Frage wurde mir heute bei einem Interview gestellt und der Interviewer ging immer wieder zurück. "Bist du sicher, dass der endlich nicht angerufen wird?" Ich war mir nicht sicher, ob es sich um eine Trickfrage handelte oder ob der Interviewer etwas anderes im Sinn hatte und schrieb den falschen Code zum Debuggen, also kam ich nach Hause und versuchte es (Erstellen und Ausführen, keine Debugger-Interaktion), nur um mich zu überlegen sich ausruhen.


Es sei denn, die ausgelöste Ausnahme wird in einem anderen Fang irgendwo höher im Stapel abgefangen. In diesem Fall wird sie möglicherweise ausgeführt, wenn diese ausgelöste Ausnahme behandelt wird ... Oder ich
liege

@ Tomosius, ja, das erklärt Brandons Antwort. :)
Andrew

@tomosius Deshalb habe ich zunächst "Wenn es eine nicht behandelte Ausnahme gibt" angegeben. Wenn die ausgelöste Ausnahme irgendwo abgefangen wird, handelt es sich per Definition um einen anderen Fall.
Eusebio Rufian-Zilbermann

Das ist nicht wahr. Zumindest nicht für NET Core 3.1. Ein einfaches neues Konsolenprojekt mit diesem Code zeigt "in the finally" nach der Ausnahme.
Emzero

Interessant, dass sich das Verhalten geändert hat, existierte .NET Core nicht einmal, als ich gepostet habe;)
Eusebio Rufian-Zilbermann

2

Eine einfache Möglichkeit, dies festzustellen, besteht darin, Ihren Code zu debuggen und zu bemerken, wann er schließlich aufgerufen wird.


1

Beim Testen mit einer C # -Konsolenanwendung wurde der finally-Code ausgeführt, nachdem die Ausnahme ausgelöst wurde: Das Dialogfeld "Anwendungsfehler" war vorhanden, und nachdem Sie die Option "Programm schließen" ausgewählt haben, wurde der finally-Block in diesem Konsolenfenster ausgeführt. Aber wenn ich die Bruchstelle innerhalb des endgültigen Codeblocks setze, kann ich sie niemals treffen. Der Debugger bleibt bei der throw-Anweisung stehen. Hier ist mein Testcode:

    class Program
    {
       static void Main(string[] args)
       {
          string msg;
          Console.WriteLine(string.Format("GetRandomNuber returned: {0}{1}", GetRandomNumber(out msg), msg) == "" ? "" : "An error has occurred: " + msg);
       }

       static int GetRandomNumber(out string errorMessage)
       {
         int result = 0;
         try
         {
            errorMessage = "";
            int test = 0;
            result = 3/test;
            return result;
         }
         catch (Exception ex)
         {
            errorMessage = ex.Message;
            throw ex;

         }
         finally
         {
            Console.WriteLine("finally block!");
         }

       }
    }

Debuggen in VS2010 - .NET Framework 4.0

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.