c# handling try - Existe uma diferença entre “jogar” e “jogar ex”?




5 Answers

Sim, há uma diferença;

  • throw ex redefine o rastreamento de pilha (para que seus erros pareçam se originar de HandleException )
  • throw não - o ofensor original seria preservado.

    static void Main(string[] args)
    {
        try
        {
            Method2();
        }
        catch (Exception ex)
        {
            Console.Write(ex.StackTrace.ToString());
            Console.ReadKey();
        }
    }
    
    private static void Method2()
    {
        try
        {
            Method1();
        }
        catch (Exception ex)
        {
            //throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
            throw ex;
        }
    }
    
    private static void Method1()
    {
        try
        {
            throw new Exception("Inside Method1");
        }
        catch (Exception)
        {
            throw;
        }
    }
list code

Existem algumas mensagens que perguntam qual é a diferença entre as duas.
(por que eu tenho que mencionar isso ...)

Mas a minha pergunta é diferente de uma maneira que eu estou chamando de "jogar ex" em outro método de manipulação semelhante a um erro.

public class Program {
    public static void Main(string[] args) {
        try {
            // something
        } catch (Exception ex) {
            HandleException(ex);
        }
    }

    private static void HandleException(Exception ex) {
        if (ex is ThreadAbortException) {
            // ignore then,
            return;
        }
        if (ex is ArgumentOutOfRangeException) { 
            // Log then,
            throw ex;
        }
        if (ex is InvalidOperationException) {
            // Show message then,
            throw ex;
        }
        // and so on.
    }
}

Se try & catch fosse usado no Main , eu usaria throw; para relançar o erro. Mas no código simplificado acima, todas as exceções passam por HandleException

throw ex; tem o mesmo efeito que chamar throw quando chamado dentro de HandleException ?




As outras respostas estão inteiramente corretas, mas esta resposta fornece alguns detalhes adicionais, eu acho.

Considere este exemplo:

using System;

static class Program {
  static void Main() {
    try {
      ThrowTest();
    } catch (Exception e) {
      Console.WriteLine("Your stack trace:");
      Console.WriteLine(e.StackTrace);
      Console.WriteLine();
      if (e.InnerException == null) {
        Console.WriteLine("No inner exception.");
      } else {
        Console.WriteLine("Stack trace of your inner exception:");
        Console.WriteLine(e.InnerException.StackTrace);
      }
    }
  }

  static void ThrowTest() {
    decimal a = 1m;
    decimal b = 0m;
    try {
      Mult(a, b);  // line 34
      Div(a, b);   // line 35
      Mult(b, a);  // line 36
      Div(b, a);   // line 37
    } catch (ArithmeticException arithExc) {
      Console.WriteLine("Handling a {0}.", arithExc.GetType().Name);

      //   uncomment EITHER
      //throw arithExc;
      //   OR
      //throw;
      //   OR
      //throw new Exception("We handled and wrapped your exception", arithExc);
    }
  }

  static void Mult(decimal x, decimal y) {
    decimal.Multiply(x, y);
  }
  static void Div(decimal x, decimal y) {
    decimal.Divide(x, y);
  }
}

Se você descomentar o throw arithExc; linha, sua saída é:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 44
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Certamente, você perdeu informações sobre onde essa exceção aconteceu. Se você usar o throw; linha, isso é o que você ganha:

Handling a DivideByZeroException.
Your stack trace:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 46
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Isso é muito melhor, porque agora você vê que foi o método Program.Div que causou problemas. Mas ainda é difícil ver se esse problema vem da linha 35 ou da linha 37 no bloco try .

Se você usar a terceira alternativa, envolvendo uma exceção externa, você não perderá nenhuma informação:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 48
   at Program.Main() in c:\somepath\Program.cs:line 9

Stack trace of your inner exception:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 35

Em particular, você pode ver que é a linha 35 que leva ao problema. No entanto, isso exige que as pessoas pesquisem a InnerException , e parece um pouco indireto usar exceções internas em casos simples.

Neste post eles preservam o número da linha (linha do bloco try) chamando (através da reflexão) o método internal intance InternalPreserveStackTrace() no objeto Exception . Mas não é bom usar uma reflexão como essa (o .NET Framework pode alterar seus membros internal algum dia sem aviso).




Não, isso fará com que a exceção tenha um rastreamento de pilha diferente. Somente o uso de um throw sem qualquer objeto de exceção no manipulador de catch deixará o rastreio de pilha inalterado.

Você pode querer retornar um booleano de HandleException se a exceção deve ser relançada ou não.




MSDN significa :

Uma vez que uma exceção é lançada, parte das informações que ela carrega é o rastreamento de pilha. O rastreamento de pilha é uma lista da hierarquia de chamada do método que começa com o método que lança a exceção e termina com o método que captura a exceção. Se uma exceção for lançada especificando a exceção na instrução throw, o rastreamento de pilha é reiniciado no método atual e a lista de chamadas de método entre o método original que executou a exceção e o método atual é perdida. Para manter as informações de rastreio de pilha originais com exceção, use a instrução throw sem especificar a exceção.




Para lhe dar uma perspectiva diferente sobre isso, usar o throw é particularmente útil se você estiver fornecendo uma API para um cliente e quiser fornecer informações detalhadas de rastreamento de pilha para sua biblioteca interna. Usando throw aqui, eu obteria o rastreamento de pilha nesse caso da biblioteca System.IO.File para File.Delete. Se eu usar throw ex, então essa informação não será passada para o meu manipulador.

static void Main(string[] args) {            
   Method1();            
}

static void Method1() {
    try {
        Method2();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method1");             
    }
}

static void Method2() {
    try {
        Method3();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method2");
        Console.WriteLine(ex.TargetSite);
        Console.WriteLine(ex.StackTrace);
        Console.WriteLine(ex.GetType().ToString());
    }
}

static void Method3() {
    Method4();
}

static void Method4() {
    try {
        System.IO.File.Delete("");
    } catch (Exception ex) {
        // Displays entire stack trace into the .NET 
        // or custom library to Method2() where exception handled
        // If you want to be able to get the most verbose stack trace
        // into the internals of the library you're calling
        throw;                
        // throw ex;
        // Display the stack trace from Method4() to Method2() where exception handled
    }
}



Related