C # Tempo de finalmente execução

votos
21

Tome este código:

using System;

namespace OddThrow
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                throw new Exception(Exception!);
            }
            finally
            {
                System.Threading.Thread.Sleep(2500);
                Console.Error.WriteLine(I'm dying!);
                System.Threading.Thread.Sleep(2500);
            }
        }
    }
}

O que me dá essa saída:

Unhandled Exception: System.Exception: Exception!
   at OddThrow.Program.Main(String[] args) in C:\Documents and Settings\username
\My Documents\Visual Studio 2008\Projects\OddThrow\OddThrow\Program.cs:line 14
I'm dying!

Minha pergunta é: por que o texto de exceção não tratada ocorrer antes do finalmente é? Em minha mente, a, finalmente, deve ser excuted como a pilha desenrola, antes mesmo de saber que esta excepção não for tratada. Observe as chamadas dormir () - estes ocorrem após a excepção não é impresso, como se ele estivesse fazendo isso seguinte:

  1. texto de exceção não tratada / mensagem
  2. Finalmente blocos.
  3. cessar a aplicação

De acordo com o padrão C #, §8.9.5, esse comportamento está errado:

  • No membro função atual, cada tentativa declaração que inclui o ponto de lançamento é examinado. Para cada declaração S, começando com a instrução try mais interna e terminando com a instrução try externa, as seguintes etapas são avaliados:
    • Se o bloco de teste de S inclui o ponto de lançamento e se S tem uma ou mais cláusulas de captura, as cláusulas de captura são examinadas por ordem de aparecimento de localizar um manipulador adequado para a excepção. A primeira cláusula de captura que especifica o tipo de excepção ou um tipo de base do tipo de excepção é considerado um jogo. A cláusula de captura geral (§8.10) é considerada uma correspondência para qualquer tipo de excepção. Se uma cláusula catch correspondente está localizado, a propagação de exceção é concluída por transferência de controlo para o bloco de cláusula que captura.
    • Caso contrário, se o bloco de teste ou um bloco de captura de S inclui o ponto de lançamento e se S tem um bloco finalmente, o controlo é transferido para o último bloco. Se o bloco finalmente joga outra excepção, processamento de excepção actual é terminada. Caso contrário, quando o controlo atinge o ponto final do último bloco, processamento de excepção corrente continua.
  • Se um manipulador de exceção não foi localizado no atual invocação membro função, a chamada função membro é encerrado. Os passos acima são então repetidas para o chamador do elemento de função com um ponto de lançamento correspondente à comunicação a partir do qual o elemento de função foi invocada.
  • Se o processamento de exceção termina todas as invocações membros função no segmento atual, indicando que o segmento tem nenhum manipulador para a exceção, em seguida, o segmento é a própria encerrado. O impacto de tais terminação é definido pela implementação.

Onde eu estou errando? (Eu tenho algumas mensagens de erro do console costume, e este é in-the-way. Minor, apenas irritante, e fazendo-me questionar a linguagem ...)

Publicado 19/05/2009 em 21:20
fonte usuário
Em outras línguas...                            


10 respostas

votos
7

As declarações do padrão sobre a ordem de execução estão corretas, e não inconsistente com o que você está observando. A mensagem "Excepção" é permitido aparecer em qualquer ponto do processo, porque é apenas uma mensagem do CLR, não realmente um manipulador de exceção em si. As regras sobre a ordem de execução se aplicam apenas ao código que está sendo executado dentro do CLR, não ao que o próprio CLR faz.

O que você realmente feito é expor um detalhe de implementação, o que é que exceções não tratadas são reconhecidos por olhar para uma pilha de que try {} blocos que estão dentro, ao invés de realmente explorar todo o caminho até a raiz. Exceções podem ou não podem ser tratadas por olhar para esta pilha, mas não manipulado exceções são reconhecidos desta forma.

Como você pode estar ciente, se você colocar uma tentativa de nível superior {} catch {} na sua principal função, então você vai ver o comportamento que você espera: cada função está finalmente será executado antes de verificar o próximo quadro para um catch correspondente { }.

Respondeu 19/05/2009 em 21:53
fonte usuário

votos
2

Para adicionar mais na mistura, considere o seguinte:

using System;
namespace OddThrow
{
    class Program
    {
        static void Main()
        {
            AppDomain.CurrentDomain.UnhandledException +=
                delegate(object sender, UnhandledExceptionEventArgs e)
            {
                Console.Out.WriteLine("In AppDomain.UnhandledException");
            };
            try
            {
                throw new Exception("Exception!");
            }
            catch
            {
                Console.Error.WriteLine("In catch");
                throw;
            }
            finally
            {
                Console.Error.WriteLine("In finally");
            }
        }
    }
}

Que no meu sistema (norueguês) mostra isso:

[C:\..] ConsoleApplication5.exe
In catch
In AppDomain.UnhandledException

Ubehandlet unntak: System.Exception: Exception!
   ved OddThrow.Program.Main() i ..\Program.cs:linje 24
In finally
Respondeu 19/05/2009 em 21:49
fonte usuário

votos
2

Acho que este artigo pode ajudar você a entender mais detalhadamente o que está acontecendo aqui.

http://bartdesmet.net/blogs/bart/archive/2006/04/04/clr-exception-handling-from-a-to-z-everything-you-didn-t-want-to-know-about- try-catch-finalmente-fault-filter.aspx

Respondeu 19/05/2009 em 21:47
fonte usuário

votos
2

A saída é na verdade a partir do manipulador de exceção CLR padrão. Manipuladores de exceção ocorrer antes do bloco finally. Após o último bloco a CLR termina devido à excepção não processada (isto não pode terminar antes, como c # garantias [1] que o finalmente cláusula é chamado).

Então, eu diria que é apenas o comportamento padrão, a manipulação de exceção ocorre antes de finalmente.

[1] guranteed durante a operação normal, pelo menos, na ausência de erros de execução internos ou falha de energia

Respondeu 19/05/2009 em 21:36
fonte usuário

votos
2

Eu poderia estar longe de base na forma como eu estou lendo coisas ... mas você tem uma chance bloco finally sem um prendedor.

Juding pela descrição que você postou, já que a exceção nunca é pego, ele borbulha para o chamador, trabalha a sua maneira através da pilha, é, eventualmente, não tratada, a chamada termina, e então o último bloco é chamado.

Respondeu 19/05/2009 em 21:35
fonte usuário

votos
1

Apesar de não ser totalmente esperado, o programa se comporta como deveria. Um último bloco não é esperado para ser executado em primeiro lugar, é só esperar que ele seja executado sempre.

Ajustei o seu exemplo:

public static void Main()
{
    try
    {
        Console.WriteLine("Before throwing");
        throw new Exception("Exception!");
    }
    finally
    {
        Console.WriteLine("In finally");
        Console.ReadLine();
    }
}

Neste caso, você receberá o diálogo excepção não desagradável, mas depois a saída do console vontade e esperar por entrada, executando assim o finalmente, não apenas antes de janelas em si chama a exceção não tratada.

Respondeu 19/05/2009 em 21:37
fonte usuário

votos
0

Próxima tentativa:

  1. Eu acredito que este caso não é mencionado no padrão c # e eu concordo que parece quase contradizê-la.

  2. Acredito que a razão interna porque isso está acontecendo é um pouco assim: O CLR registra seu manipulador de exceção padrão como manipulador SEH em FS: [0] quando você tem mais capturas no seu código, esses manipuladores são adicionados à cadeia SEH. Alternativamente, apenas o manipulador CLR é chamado durante o manuseio SEH e manipula a cadeia exceção CLR internamente, eu não sei qual.

Em seu código quando a exceção é lançada, apenas o manipulador padrão é na cadeia SEH. Este manipulador é chamado antes de qualquer desenrolar pilha começa.

O manipulador de exceção padrão sabe que não há nenhum manipulador de exceção registrado na pilha. Por isso, chama todos manipulador UnhandledException registado pela primeira vez, em seguida, imprime a sua mensagem de erro e marca o AppDomain para o descarregamento.

Só depois que a pilha desenrolar mesmo de começar e, finalmente blocos são chamados de acordo com o padrão c #.

A meu ver, a forma como o CLR lida com exceção sem tratamento não é considerado no padrão c #, apenas a ordem em que Finallys são chamados durante o desenrolamento de pilha. Esta ordem é preservada. Depois de que o "O impacto de tais terminação é definido pela implementação." cláusula tem efeito.

Respondeu 19/05/2009 em 22:09
fonte usuário

votos
0

Para colocar um par de respostas em conjunto, o que acontece é que, assim que você tem uma exceção não tratada um UnhandledExceptionEvent é levantada sobre o AppDomain, em seguida, o código continua a executar (ou seja, o finalmente). Este é o artigo MSDN sobre o evento

Respondeu 19/05/2009 em 22:02
fonte usuário

votos
0

Um try / finally sem uma captura usará o manipulador padrão que faz exatamente o que você vê. Eu usá-lo o tempo todo, por exemplo, nos casos em manipulação a exceção seria cobrindo um erro, mas ainda há alguma limpeza que você quer fazer.

Lembre-se também que a saída para o erro padrão e fora padrão são tamponados.

Respondeu 19/05/2009 em 21:37
fonte usuário

votos
0

O try-catch-finally blocos estão trabalhando exatamente como você esperava , se forem apanhados em algum momento . Quando eu escrevi um programa de teste para isso, e eu usar vários níveis de aninhamento, o único caso que se comportou de uma maneira que combinava com o que você descreveu foi quando a exceção foi completamente não tratada pelo código, e ele borbulhava para o sistema operacional.

Cada vez que eu corri, a OS foi o que criou a mensagem de erro. Assim, a questão não é com C #, é com o fato de que um erro que é não tratada pelo código do usuário é não mais sob o controle do aplicativo e, portanto, o tempo de execução (creio eu) não pode forçar um padrão de execução nele.

Se você criou um aplicativo de formulário do Windows, e escreveu todas as suas mensagens para uma caixa de texto (logo em seguida rubor-los) em vez de escrever diretamente para o console, você não teria visto que a mensagem de erro em tudo, porque ele foi inserido no console de erros pelo aplicativo de chamada e não por seu próprio código.

EDITAR

Vou tentar destacar a parte fundamental disso. Exceções sem tratamento estão fora de seu controle, e você não pode determinar quando seu manipulador de exceção será executada. Se você capturar a exceção em algum momento em sua aplicação, em seguida, os finallyblocos serão executados antes do baixo-in-the-stack catchbloco.

Respondeu 19/05/2009 em 21:29
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more