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




working with exceptions c# (7)

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 ?

https://code.i-harness.com


(Eu postei anteriormente e @Marc Gravell me corrigiu)

Aqui está uma demonstração da diferença:

static void Main(string[] args) {
    try {
        ThrowException1(); // line 19
    } catch (Exception x) {
        Console.WriteLine("Exception 1:");
        Console.WriteLine(x.StackTrace);
    }
    try {
        ThrowException2(); // line 25
    } catch (Exception x) {
        Console.WriteLine("Exception 2:");
        Console.WriteLine(x.StackTrace);
    }
}

private static void ThrowException1() {
    try {
        DivByZero(); // line 34
    } catch {
        throw; // line 36
    }
}
private static void ThrowException2() {
    try {
        DivByZero(); // line 41
    } catch (Exception ex) {
        throw ex; // line 43
    }
}

private static void DivByZero() {
    int x = 0;
    int y = 1 / x; // line 49
}

e aqui está a saída:

Exception 1:
   at UnitTester.Program.DivByZero() in <snip>\Dev\UnitTester\Program.cs:line 49
   at UnitTester.Program.ThrowException1() in <snip>\Dev\UnitTester\Program.cs:line 36
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 19

Exception 2:
   at UnitTester.Program.ThrowException2() in <snip>\Dev\UnitTester\Program.cs:line 43
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 25

Você pode ver que na Exceção 1, o rastreamento de pilha retorna ao método DivByZero() , enquanto na Exceção 2 isso não acontece.

Observe, porém, que o número da linha mostrado em ThrowException1() e ThrowException2() é o número da linha da instrução throw , não o número da linha da chamada para DivByZero() , o que provavelmente faz sentido agora pouco...

Saída no modo Release

Exceção 1:

at ConsoleAppBasics.Program.ThrowException1()
at ConsoleAppBasics.Program.Main(String[] args)

Exceção 2:

at ConsoleAppBasics.Program.ThrowException2()
at ConsoleAppBasics.Program.Main(String[] args)

Mantém o stackTrace original apenas no modo de depuração?


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).


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
    }
}

Quando você joga ex, aquela exceção lançada se torna a "original". Portanto, todo o rastreamento de pilha anterior não estará lá.

Se você jogar, a exceção vai pela linha e você obterá o rastreamento completo da pilha.


Veja aqui: http://blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html

Jogue :

try 
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw;
}

Preservar as informações da pilha com exceção

Isso é chamado de "Rethrow"

Se quiser lançar nova exceção,

throw new ApplicationException("operation failed!");

Jogue Ex :

try
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw ex;
}

Não enviará informações de pilha com exceção

Isso é chamado de "Quebrando a pilha"

Se quiser lançar nova exceção,

throw new ApplicationException("operation failed!",ex);

vamos entender a diferença entre jogar e jogar ex. Ouvi dizer que, em muitas entrevistas em rede, este comum pedido está sendo perguntado.

Apenas para dar uma visão geral desses dois termos, throw e throw ex são usados ​​para entender onde a exceção ocorreu. Lançar ex reescreve o rastreio de exceção de pilha independentemente de onde realmente foi lançado.

Vamos entender com um exemplo.

Vamos entender primeiro o lançamento.

static void Main(string[] args) {
    try {
        M1();
    } catch (Exception ex) {
        Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------");
        Console.WriteLine(ex.StackTrace.ToString());
        Console.WriteLine(" ---------------- Method Name / Target Site -------------- ");
        Console.WriteLine(ex.TargetSite.ToString());
    }
    Console.ReadKey();
}

static void M1() {
    try {
        M2();
    } catch (Exception ex) {
        throw;
    };
}

static void M2() {
    throw new DivideByZeroException();
}

saída do acima está abaixo.

mostra hierarquia completa e nome do método onde a exceção foi lançada .. é M2 -> M2. junto com números de linha

Em segundo lugar .. vamos entender por jogar ex. Apenas substitua throw por throw ex no bloco catch do método M2. como abaixo.

saída de throw ex código é como abaixo ..

Você pode ver a diferença na saída .. throw ex apenas ignora toda a hierarquia anterior e redefine o rastreamento da pilha com a linha / método onde throw ex é gravado.


int a = 0;
try {
    int x = 4;
    int y ;
    try {
        y = x / a;
    } catch (Exception e) {
        Console.WriteLine("inner ex");
        //throw;   // Line 1
        //throw e;   // Line 2
        //throw new Exception("devide by 0");  // Line 3
    }
} catch (Exception ex) {
    Console.WriteLine(ex);
    throw ex;
}
  1. se todas as linhas 1, 2 e 3 forem comentadas - Output - interior ex

  2. se todas as linhas 2 e 3 forem comentadas - Saída - interna ex System.DevideByZeroException: {"Tentativa de dividir por zero"} ---------

  3. se todas as linhas 1 e 2 forem comentadas - Saída - interna ex System.Exception: devide por 0 ----

  4. se todas as linhas 1 e 3 forem comentadas - Saída - interna ex System.DevideByZeroException: {"Tentativa de dividir por zero."} ---------

e StackTrace será resetado no caso de throw ex;





exception-handling