[Captura de exceção em C#] A instrução Try-Catch em C# realmente afeta o desempenho do programa?

Muitas postagens analisaram o mecanismo Try-Catch e seu impacto no desempenho.

Entretanto, não há evidências de que o Try-Catch consuma muito desempenho do sistema, especialmente em um ambiente hospedado. Lembro-me que um internauta no jardim usou o StopWatch para analisar os indicadores de tempo de execução do código Try-Catch em diferentes circunstâncias em comparação com o código sem Try-Catch, e os resultados não foram muito diferentes.

Deixe-me analisar o Try-Catch em combinação com o IL.

● Análise do mecanismo

O mecanismo básico de captura e tratamento de exceções em .Net é completado pelos blocos try...catch...finally, que completam respectivamente o monitoramento, captura e processamento de exceções. Um bloco try pode corresponder a zero ou mais blocos catch e pode corresponder a zero ou um bloco finalmente. No entanto, uma tentativa sem captura parece sem sentido. Se a tentativa corresponder a múltiplas capturas, depois de detectar uma exceção, o CLR pesquisará o código do bloco catch de cima para baixo e filtrará a exceção correspondente através do filtro de exceção. Se for não for encontrado, então o CLR procurará exceções correspondentes ao longo da pilha de chamadas para níveis mais altos. Se a exceção correspondente ainda não for encontrada no topo da pilha, uma exceção não tratada será lançada. Neste momento, o código no O bloco catch não será executado. Portanto, o bloco catch mais próximo de try será percorrido primeiro.

Se houver o seguinte código:

    try
   {
    
    
       Convert.ToInt32("Try");
   }
       catch (FormatException ex1)
   {
    
    
       string CatchFormatException = "CatchFormatException";
   }
       catch (NullReferenceException ex2)
   {
    
    

       string CatchNullReferenceException = "CatchNullReferenceException";
   }

   finally
   {
    
    
       string Finally = "Finally";
   }

O IL correspondente é o seguinte:

.method private hidebysig instance void Form1_Load(object sender,
class [mscorlib]System.EventArgs e) cil managed
{
    
    
// Code size 53 (0x35)
.maxstack 1
.locals init ([0] class [mscorlib]System.FormatException ex1,
[1] string CatchFormatException,
[2] class [mscorlib]System.NullReferenceException ex2,
[3] string CatchNullReferenceException,
[4] string Finally)
IL_0000: nop
IL_0001: nop
IL_0002: ldstr "Try"
IL_0007: call int32 [mscorlib]System.Convert::ToInt32(string)
IL_000c: pop
IL_000d: nop
IL_000e: leave.s IL_0026
IL_0010: stloc.0
IL_0011: nop
IL_0012: ldstr "CatchFormatException"
IL_0017: stloc.1
IL_0018: nop
IL_0019: leave.s IL_0026
IL_001b: stloc.2
IL_001c: nop
IL_001d: ldstr "CatchNullReferenceException"
IL_0022: stloc.3
IL_0023: nop
IL_0024: leave.s IL_0026
IL_0026: nop
IL_0027: leave.s IL_0033
IL_0029: nop
IL_002a: ldstr "Finally"
IL_002f: stloc.s Finally
IL_0031: nop
IL_0032: endfinally
IL_0033: nop
IL_0034: ret
IL_0035:
// Exception count 3
.try IL_0001 to IL_0010 catch [mscorlib]System.FormatException handler IL_0010 to IL_001b
.try IL_0001 to IL_0010 catch [mscorlib]System.NullReferenceException handler IL_001b to IL_0026
.try IL_0001 to IL_0029 finally handler IL_0029 to IL_0033
} // end of method Form1::Form1_Load

As últimas linhas de código revelam como o IL lida com o tratamento de exceções. Cada item nas últimas três linhas é chamado de cláusula de tratamento de exceção, o EHC forma a tabela de tratamento de exceção e o EHT é separado do código normal pela instrução ret return.

Pode-se ver que FormatException está classificado em primeiro lugar no EHT.

Quando o código é executado com sucesso ou retorna vice-versa, o CLR atravessa o EHT:

1. Se uma exceção for lançada, o CLR encontrará o EHC correspondente de acordo com o "endereço" do código que lança a exceção (IL_0001 a IL_0010 é o intervalo do código de detecção). Neste exemplo, o CLR encontrará 2 EHCs e FormatException serão percorridos primeiro e é um EHC adequado.

2. Se o endereço do código retornado estiver entre IL_0001 a IL_0029, o manipulador final, ou seja, o código de IL_0029 a IL_0033 também será executado, independentemente de ser retornado devido à execução bem-sucedida do código.

Na verdade, o trabalho de travessia de catch e finalmente é executado separadamente. Como mencionado acima, a primeira coisa que o CLR faz é percorrer o bloco catch. Quando o bloco catch apropriado é encontrado, ele então percorre o bloco correspondente finalmente; e este processo será executado recursivamente por pelo menos duas vezes, porque o compilador traduz o try...catch...finally do C# em dois níveis de aninhamento em IL.

Obviamente, se o bloco catch correspondente não for encontrado, o CLR será executado finalmente e imediatamente interromperá todos os threads. O código no bloco Finalmente será definitivamente executado independentemente de try detectar uma exceção.

Sugestões para melhorar

Pode-se concluir do conteúdo acima:

Se "Try-Catch" for usado e uma exceção for capturada, tudo o que o CLR faz é percorrer os itens Catch na Tabela de Tratamento de Exceções; em seguida, ele percorre os itens Finalmente na Tabela de Tratamento de Exceções novamente, e quase todo o tempo é gasto percorrendo a Tabela de Tratamento de Exceções. e se nenhuma exceção for capturada, o CLR apenas percorre os itens Finalmente na Tabela de Tratamento de Exceções, e o tempo necessário é mínimo.

O tempo necessário para executar a operação correspondente após a travessia "Try-Catch" é determinado de acordo com seu código específico. "Try-Catch" causa apenas monitoramento e acionamento, e esta parte do tempo de código não deve ser contada como "Try- Pegar". consumo.

Portanto, podemos considerar tanto o desempenho quanto a revisão do código. Geralmente, as seguintes diretrizes são recomendadas:

1. Tente fornecer ao CLR informações claras sobre exceções e não use Exception para filtrar exceções.

2. Tente não escrever try...catch em um loop

3. Tente usar o mínimo de código possível. Se necessário, você pode usar vários blocos catch e escrever o tipo de exceção com maior probabilidade de ser lançado na posição mais próxima da tentativa.

4. Não declare apenas um objeto Exception sem manipulá-lo. Fazer isso aumenta o comprimento da Tabela de Tratamento de Exceções em vão.

5. Use as "Exceções CLR" do utilitário contador de desempenho para detectar exceções e otimizar adequadamente

6. Use o modo Try-Parse do membro. Se uma exceção for lançada, substitua-a por false.

Concluindo, embora o Try-Catch consuma um pouco de tempo, os programadores não precisam falar sobre isso. Pela análise acima, não é tanto que o "Try-Catch" perca ou afete o desempenho. É melhor dizer que "Try-Catch" é como outros códigos. Consumidores comuns de desempenho, mas para fins de revisão da escrita do código, tente prestar atenção em "Try-Catch".

Sinto-me como a análise do chefe, com o link original em anexo: https://m.xp.cn/b.php/75621.html

おすすめ

転載: blog.csdn.net/weixin_44737486/article/details/132165450