Quando é finalmente executado, se você lançar uma exceção do bloco catch?


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

No bloco acima, quando o bloco finalmente é chamado? Antes do lançamento de e ou é finalmente chamado e depois capturado?


14
ps você não deve "jogar e;" porque isso atrapalha o rastreamento da pilha da exceção original. Você deve apenas "jogar"; Ou crie uma nova exceção e defina a InnerException como "e" antes de lançá-la.
22411 Erv Walter

24
finalmente, seria uma péssima escolha de palavra-chave, se não fosse a última , não diria?
11139 Eric Lippert

@ErvWalter isso ainda é verdade? Estou testando nos dois sentidos no VS2017 e parece ser exatamente o mesmo. Você poderia fornecer mais algumas informações ou uma referência? Obrigado
Jeff Puckett

apenas nomear sugestão use Exceção ex - reserve e para eventos / delegados
sr. R

Respostas:


138

Seria chamado depois que e for jogado novamente (ou seja, depois que o bloco catch for executado)

editando isso 7 anos depois - uma observação importante é que, se enão for capturado por um bloco try / catch mais adiante na pilha de chamadas ou tratado por um manipulador de exceção global, o finallybloco poderá nunca ser executado.


18
e nunca se você chamar Envrionment.FailFast ()
Johannes Rudolph

16
Depois de tentar o código na resposta de Brandon, eu vejo que o finallynão é executado se a exceção lançada no anterior catchnunca é pego em uma externa try- catchbloco!
21815 Andrew Andrew

3
Obrigado por editar sua resposta (aceita) para incluir as novas informações.
Gordon Bean

3
Eles (Microsoft) falam sobre isso no novo site de documentação: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… : "Dentro de uma exceção manipulada, o bloco finalmente associado é garantido para ser executado. , se a exceção não for tratada, a execução do bloco final depende de como a operação de desenrolamento da exceção é acionada. Isso, por sua vez, depende de como o computador está configurado. "
DotNetSparky

1
Observe que "capturado por um bloco try / catch além da pilha de chamadas" incluirá manipuladores de estrutura, como os do ASP.NET ou um executor de teste. Uma maneira melhor de colocá-lo pode ser "se o seu programa continuar em execução após o bloco catch, o bloco finalmente será executado".
ArrowCase 21/10/19

91

Por que não tentar:

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

com código (formatado para espaço vertical):

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, para algo tão simples, você deve realmente tentar como Marc. GJ ilustrando-o com aninhada tentativa / captura / finalmente :)
Allen Arroz

1
@AllenRice Por que tentar se Marc já tem, e eu posso pesquisar no Google para encontrar a resposta de Marc? Ou talvez melhor, tente você mesmo, crie uma pergunta SO e responda você mesmo, para benefício dos outros.
joshden

8
Observe que, se você não capturar a exceção na captura externa, o interior finalmente NUNCA é executado !! Nesse caso, a saída éouter try inner try inner catch Unhandled Exception: System.DivideByZeroException...
Andrew

1
@ Andrew você está certo. A explicação que você pode encontrar aqui MSDN Magazine 2008 September: Processamento de exceções sem tratamento no CLR (para abrir o chm, é necessário desbloquear: Propriedades do arquivo -> Geral -> Desbloqueio). Se você substituir o bloco de captura externo por "catch (ArgumentException)", ninguém finalmente continuará o bloco também, porque o CLR não poderá encontrar nenhum "manipulador de exceção que concordou em manipular a exceção" DivideByZeroException.
vladimir

35

Depois de ler todas as respostas aqui, parece que a resposta final é que depende :

  • Se você repetir uma exceção dentro do bloco catch, e essa exceção for capturada dentro de outro bloco catch, tudo será executado de acordo com a documentação.

  • No entanto, se a exceção retrocedida não for tratada, ela finalmente nunca será executada.

Testei este exemplo de código no VS2010 com 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();
    }

Aqui está a saída:

Exemplo 1: lance novamente dentro de outro bloco de tentativa:
--outer try
---- try
interior
---- catch interno ---- inner finalmente
--outer catch
--outer finalmente
Huzzah!

Exemplo 2: lance novamente fora de outro bloco de tentativa:
--try
--catch

Exceção sem tratamento: System.Exception: exceção do tipo 'System.Exception' foi lançada.
em ConsoleApplication1.Program.Main () em C: \ source local \ ConsoleApplication1 \ Program.cs: linha 53


3
Ótima captura, eu não estava ciente disso!
Andrew

1
Observe que o último finalmente pode ser executado, dependendo do que você escolher: stackoverflow.com/a/46267841/480982
Thomas Weller

1
Interessante ... No .NET Core 2.0, a parte finalmente é executada após a exceção não tratada.
Mahdi Ghiasi

Curiosamente, acabei de fazer um teste no .NET Core 2.0 e no .NET Framework 4.6.1 e os dois executam a exceção finalmente após a manipulação. Esse comportamento mudou?
Cameron Bielstein

24

Seu exemplo se comportaria de forma idêntica a este código:

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

Como uma observação lateral, se você realmente quer dizer throw e;(ou seja, lançar a mesma exceção que capturou), é muito melhor fazer isso throw;, pois isso preservará o rastreamento da pilha original em vez de criar um novo.


Eu não acho que isso esteja correto. Finalmente deve estar dentro do bloco try exterior não fora dela
Matthew Pigram

@MatthewPigram: Como assim? De fato, o finallybloco será executado após o catchbloco (mesmo que o bloco catch repita a exceção), que é o que meu snippet está tentando ilustrar.
Daniel Pryden

pelo modo como interpreto o exemplo dele, ele está tentando tentar, finalmente, dentro de outro bloco de tentativa. NÃO um try catch dentro de um try catch finalmente
Matthew Pigram

1
@ MatthewPigram: Minha resposta não tem nenhuma construção "try-catch-finalmente". Ele tem um "try-finalmente" e, dentro do trybloco, minha resposta tem um "try-catch". Estou tentando explicar o comportamento da construção em três partes usando duas construções em duas partes. Não vejo nenhum sinal de um segundo trybloco na pergunta original, então não entendo de onde você está conseguindo isso.
Daniel Pryden

12

Se houver uma exceção não tratada dentro de um bloco de manipulador de captura, o bloco finalmente será chamado exatamente zero vezes

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

Resultado:

C: \ usuários \ administrador \ documentos \ TestExceptionNesting \ bin \ Release> TestExceptionNesting.exe

na tentativa

na captura

Exceção não tratada: System.DivideByZeroException: tentativa de dividir por zero. em TestExceptionNesting.Program.Main (String [] args) em C: \ users \ administrator \ documents \ TestExceptionNesting \ TestExceptionNesting.cs: linha 22

C: \ usuários \ administrador \ documentos \ TestExceptionNesting \ bin \ release>

Fiz essa pergunta hoje em uma entrevista e o entrevistador continuou voltando "você tem certeza de que finalmente não foi chamado?" Eu não tinha certeza se era uma pergunta complicada ou se o entrevistador tinha outra coisa em mente e escreveu o código errado para eu depurar, então cheguei em casa e tentei (construir e executar, sem interação do depurador), apenas para colocar minha mente em descansar.


A menos que a exceção lançada é pego em outro prendedor algum lugar mais acima na pilha, caso em que pode executar se que exceção lançada é tratada ... Ou eu poderia estar errado ...
tomosius

@tomosius, sim, é isso que a resposta de Brandon explica. :) #
Andrew Andrew

@tomosius É por isso que comecei especificando "Se houver uma exceção não tratada". Se a exceção lançada for capturada em algum lugar, então, por definição, estamos falando de um caso diferente.
Eusebio rufián-Zilbermann

Isso não é verdade. Pelo menos não para o NET Core 3.1. Um novo projeto simples de console com esse código mostra "no final" após a exceção.
emzero 01/04

Interessante que o comportamento tenha mudado, o núcleo do .NET nem existia quando eu
publiquei

2

Uma maneira simples de dizer também é depurar seu código e perceber quando finalmente é chamado.


1

Testando com um aplicativo de console C #, o código finalmente foi executado após a exceção: A "caixa de diálogo Erro de aplicativo" existia e depois que você selecionava a opção "Fechar o programa", o bloco finalmente foi executado nessa janela do console. Mas definindo o ponto de ruptura dentro do bloco de código finalmente, nunca consigo atingi-lo. O depurador continua parando na instrução throw. Aqui está o meu código de teste:

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

       }
    }

Depurando no VS2010 - .NET Framework 4.0

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.