Quando viene infine eseguito se si genera un'eccezione dal blocco catch?


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

Nel blocco sopra quando viene chiamato il blocco finally? Prima del lancio di eo viene infine chiamato e poi catturato?


14
ps non dovresti "lanciare e;" perché ciò rovinerà la traccia dello stack dell'eccezione originale. Dovresti semplicemente "lanciare;". Oppure crea una nuova eccezione e imposta InnerException su "e" prima di lanciarla.
Erv Walter,

24
alla fine sarebbe una scelta piuttosto scadente della parola chiave se non fosse eseguita per ultima , non diresti?
Eric Lippert,

@ErvWalter è ancora vero? Lo sto testando in entrambi i modi in VS2017 e sembra essere esattamente lo stesso. Potresti fornire qualche informazione in più o un riferimento? Grazie
Jeff Puckett,

solo suggerimento di denominazione utilizzare Eccezione ex - riserva e per eventi / delegati
sig. R

Risposte:


138

Verrà richiamato dopo la rilancio di e (ovvero dopo l'esecuzione del blocco catch)

modificandolo 7 anni dopo, una nota importante è che se enon viene catturato da un blocco try / catch nello stack di chiamate o gestito da un gestore di eccezioni globale, il finallyblocco potrebbe non essere mai eseguito.


18
e mai se chiami Envrionment.FailFast ()
Johannes Rudolph,

16
Dopo aver provato il codice nella risposta di Brandon, vedo che il finallynon viene eseguito se l'eccezione generata nel precedente catchnon è mai catturato in un esterno try- catchblocco!
Andrew,

3
Grazie per aver modificato la risposta (accettata) per includere le nuove informazioni.
Gordon Bean,

3
Ne parlano (Microsoft) sul nuovo sito di documentazione: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… : "All'interno di un'eccezione gestita, il blocco finalmente associato è garantito per essere eseguito. Tuttavia , se l'eccezione non viene gestita, l'esecuzione del blocco finally dipende da come viene attivata l'operazione di svolgimento dell'eccezione. Ciò, a sua volta, dipende dalla configurazione del computer. "
DotNetSparky,

1
Si noti che "catturato da un blocco try / catch più in alto nello stack di chiamate" includerà gestori di framework come quelli in ASP.NET o un runner di test. Un modo migliore per dirlo potrebbe essere "se il programma continua a essere eseguito dopo il blocco catch, verrà eseguito il blocco finally".
ArrowCase,

91

Perché non provarlo:

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

con codice (formattato per spazio verticale):

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, per qualcosa di così semplice, dovresti davvero provarlo come ha fatto Marc. GJ lo illustra con un tentativo / cattura / finalmente nidificato :)
Allen Rice,

1
@AllenRice perché provarlo se Marc lo ha già fatto e posso solo google per trovare la risposta di Marc? O forse meglio, prova te stesso, quindi crea una domanda SO e rispondi tu stesso a beneficio degli altri.
joshden,

8
Si noti che se non si rileva l'eccezione nella cattura esterna, l'interno finalmente non viene MAI eseguito !! In tal caso, l'output èouter try inner try inner catch Unhandled Exception: System.DivideByZeroException...
Andrew,

1
@Andrew hai ragione. La spiegazione è disponibile qui MSDN Magazine 2008 settembre: elaborazione delle eccezioni non gestite nel CLR (per aprire chm è necessario sbloccare: Proprietà file -> Generale -> Sblocca). Se si sostituisce il blocco catch esterno con "catch (ArgumentException)", alla fine non verrà eseguito nessuno blocco, poiché CLR non riesce a trovare alcun "gestore eccezioni che abbia accettato di gestire l'eccezione" DivideByZeroException.
Vladimir

35

Dopo aver letto tutte le risposte qui sembra che la risposta finale è dipende :

  • Se si rilancia un'eccezione all'interno del blocco catch e quell'eccezione viene catturata all'interno di un altro blocco catch, tutto viene eseguito in base alla documentazione.

  • Tuttavia, se l'eccezione re-trown non viene gestita, finalmente non viene mai eseguita.

Ho provato questo esempio di codice in VS2010 con C # 4.0

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();
    }

Ecco l'output:

Esempio 1: ri-lanciare all'interno di un altro blocco try:
--outer try
---- inner try
---- inner catch
---- inner finally
--outer catch
--outer finalmente
Huzzah!

Esempio 2: rilancia all'esterno di un altro blocco try:
--try
--catch

Eccezione non gestita: System.Exception: è stata generata un'eccezione del tipo "System.Exception".
at ConsoleApplication1.Program.Main () in C: \ local source \ ConsoleApplication1 \ Program.cs: riga 53


3
Ottima cattura, non ne ero a conoscenza!
Andrew,

1
Si noti che l'ultimo può essere eseguito, a seconda di ciò che si sceglie: stackoverflow.com/a/46267841/480982
Thomas Weller,

1
Interessante ... Su .NET Core 2.0, la parte finally viene eseguita dopo l'eccezione non gestita.
Mahdi Ghiasi,

È interessante notare che ho appena fatto un test sia in .NET Core 2.0 sia in .NET Framework 4.6.1 ed entrambi eseguono finalmente l'eccezione non gestita. Questo comportamento è cambiato?
Cameron Bielstein il

24

Il tuo esempio si comporterebbe in modo identico a questo codice:

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

Come nota a margine, se intendi davvero throw e;(ovvero, getta la stessa eccezione che hai appena catturato), è molto meglio fare semplicemente throw;, poiché ciò manterrà la traccia dello stack originale invece di crearne una nuova.


Non penso sia corretto. Alla fine dovrebbe essere all'interno del blocco di prova esterno non esterno
Matthew Pigram,

@MatthewPigram: che vuoi dire? Il finallyblocco infatti verrà eseguito dopo il catchblocco (anche se il blocco catch rinnova l'eccezione), che è ciò che il mio frammento sta cercando di illustrare.
Daniel Pryden,

da come interpreto il suo esempio, sta tentando di fare un tentativo try finalmente all'interno di un altro blocco try. NON un tentativo di cattura all'interno di un tentativo di cattura finalmente
Matthew Pigram,

1
@MatthewPigram: La mia risposta non ha alcun costrutto "try-catch-finally". Ha un "try-finally", e all'interno del tryblocco di quella la mia risposta ha un "try-catch". Sto cercando di spiegare il comportamento del costrutto in 3 parti usando due costrutti in 2 parti. Non vedo alcun segno di un secondo tryblocco nella domanda originale, quindi non capisco dove lo stai ottenendo.
Daniel Pryden,

12

Se esiste un'eccezione non gestita all'interno di un blocco del gestore catch, il blocco finally viene chiamato esattamente zero volte

  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");
     }
  }

Produzione:

C: \ Users \ Administrator \ documenti \ TestExceptionNesting \ bin \ Release> TestExceptionNesting.exe

nel tentativo

nella cattura

Eccezione non gestita: System.DivideByZeroException: tentativo di dividere per zero. at TestExceptionNesting.Program.Main (String [] args) in C: \ utenti \ amministratore \ documenti \ TestExceptionNesting \ TestExceptionNesting.cs: riga 22

C: \ Users \ amministratore bin \ release \ documenti \ TestExceptionNesting \>

Mi è stata posta questa domanda oggi durante un'intervista e l'intervistatore ha continuato a tornare "sei sicuro che finalmente non viene chiamato?" Non ero sicuro che si trattasse di una domanda trabocchetto o che l'intervistatore avesse in mente qualcos'altro e abbia scritto il codice sbagliato per il debug, quindi sono tornato a casa e l'ho provato (costruisci e corri, nessuna interazione con il debugger), solo per pensare riposo.


A meno che l'eccezione generata non venga catturata in un'altra cattura da qualche parte più in alto dello stack, nel qual caso potrebbe essere eseguita se viene gestita l'eccezione generata ... O potrei sbagliarmi ...
tomosius,

@tomosius, sì, questo è ciò che spiega la risposta di Brandon. :)
Andrew,

@tomosius Ecco perché ho iniziato specificando "Se esiste un'eccezione non gestita". Se l'eccezione generata viene rilevata da qualche parte, per definizione stiamo parlando di un caso diverso.
Eusebio Rufian-Zilbermann,

Questo non è vero. Almeno non per NET Core 3.1. Un semplice nuovo progetto console con questo codice mostra "in the finally" dopo l'eccezione.
emzero

Interessante che il comportamento sia cambiato, .NET core non esisteva nemmeno quando ho pubblicato;)
Eusebio Rufian-Zilbermann

2

Un modo semplice per dire è anche il debug del codice e notare quando finalmente viene chiamato.


1

Testando con un'applicazione console C #, il codice finally è stato eseguito dopo il lancio dell'eccezione: la "finestra di dialogo Errore applicazione" esisteva e dopo aver scelto l'opzione "Chiudi il programma", il blocco finally è stato eseguito nella finestra della console. Ma impostando il punto di rottura all'interno del blocco di codice finalmente, non riesco mai a colpirlo. Il debugger continua a fermarsi all'istruzione di lancio. Ecco il mio codice di prova:

    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!");
         }

       }
    }

Debug in VS2010 - .NET Framework 4.0

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.