diferença entre throw e throw new Exception ()


164

qual é a diferença entre

try { ... }
catch{ throw } 

e

try{ ... }
catch(Exception e) {throw new Exception(e.message) } 

Independentemente de o segundo mostrar uma mensagem?


51
O segundo trecho é uma das linhas de código mais más (mas inócuas) que eu já vi.
SLaks

Respostas:


259

throw; repete a exceção original e preserva seu rastreamento de pilha original.

throw ex;lança a exceção original, mas redefine o rastreamento de pilha, destruindo todas as informações de rastreamento de pilha até o seu catchbloco.


NUNCA escrevathrow ex;


throw new Exception(ex.Message);é ainda pior. Ele cria uma Exceptioninstância totalmente nova , perdendo o rastreamento da pilha original da exceção, bem como seu tipo. (por exemplo IOException).
Além disso, algumas exceções contêm informações adicionais (por exemplo, ArgumentException.ParamName).
throw new Exception(ex.Message); também destruirá essas informações.

Em certos casos, convém agrupar todas as exceções em um objeto de exceção personalizado, para poder fornecer informações adicionais sobre o que o código estava fazendo quando a exceção foi lançada.

Para fazer isso, defina uma nova classe que herda Exception, adicione todos os quatro construtores de exceção e, opcionalmente, um construtor adicional que use InnerExceptioninformações adicionais e também jogue sua nova classe de exceção, passando excomo InnerExceptionparâmetro . Ao passar o original InnerException, você preserva todas as propriedades da exceção original, incluindo o rastreamento da pilha.


24
"lançar nova exceção (ex); é ainda pior.": eu discordo dessa. Às vezes, você deseja alterar o tipo de uma exceção e, em seguida, manter a exceção original como exceção interna é o melhor que você pode fazer. Embora deva ser throw new MyCustomException(myMessage, ex);claro.
Dirk Vollmar

9
@ 0xA3: Eu quis dizer ex.Message, o que é pior.
SLaks

6
Além de implementar os construtores padrão, deve-se também fazer exceções personalizadas [Serializable()].
Dirk Vollmar

21
Nossa, nós agrupamos você como exceções, então colocamos uma exceção na sua exceção para que você possa capturar enquanto captura.
Continente Darth

2
@SLaks: quando você throw;o número da linha real onde ocorreu a exceção é substituído pelo número da linha de throw;. Como você sugere lidar com isso? stackoverflow.com/questions/2493779/...
Eric J.

34

O primeiro preserva o rastreamento de pilha original:

try { ... }
catch
{
    // Do something.
    throw;
}

O segundo permite alterar o tipo da exceção e / ou a mensagem e outros dados:

try { ... } catch (Exception e)
{
    throw new BarException("Something broke!");
}

Há também uma terceira maneira pela qual você passa uma exceção interna:

try { ... }
catch (FooException e) {
    throw new BarException("foo", e);
} 

Eu recomendo usar:

  • o primeiro, se você quiser fazer uma limpeza na situação de erro sem destruir informações ou adicionar informações sobre o erro.
  • o terceiro, se você quiser adicionar mais informações sobre o erro.
  • o segundo, se você deseja ocultar informações (de usuários não confiáveis).

6

Outro ponto que eu não vi ninguém fazer:

Se você não fizer nada no seu bloco de captura {}, tentar ... captura é inútil. Eu vejo isso o tempo todo:

try 
{
  //Code here
}
catch
{
    throw;
}

Ou pior:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw ex;
}

Pior ainda:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw new System.Exception(ex.Message);
}

Eu concordo, a menos que você tenha a cláusula finalmente.
Toni Rossmann

1
@ToniRossmann Nesse caso, eu usaria try..finally sem o catch, a menos que você esteja fazendo algo diferente de throw;
JLWarlow

4

throwlança novamente a exceção capturada, mantendo o rastreamento de pilha, enquanto throw new Exceptionperde alguns dos detalhes da exceção capturada.

Você normalmente usaria throwpor si só para registrar uma exceção sem manipulá-la totalmente nesse ponto.

O BlackWasp possui um bom artigo intitulado Throwing Exceptions in C # .


4

Lançar uma nova exceção afasta o rastreamento de pilha atual.

throw;manterá o rastreamento da pilha original e é quase sempre mais útil. A exceção a essa regra é quando você deseja agrupar a exceção em uma exceção personalizada de sua preferência. Você deve então fazer:

catch(Exception e)
{
    throw new CustomException(customMessage, e);
}

3

throwé para relançar uma exceção capturada. Isso pode ser útil se você quiser fazer algo com a exceção antes de passar para a cadeia de chamadas.

O uso throwsem argumentos preserva a pilha de chamadas para fins de depuração.


0

Se você quiser, pode lançar uma nova exceção, com a original definida como uma exceção interna.


0

Seu segundo exemplo redefinirá o rastreamento de pilha da exceção. O primeiro preserva com mais precisão as origens da exceção. Além disso, você desembrulhou o tipo original, essencial para saber o que realmente deu errado ... Se o segundo for necessário para a funcionalidade - por exemplo, para adicionar informações estendidas ou envolver novamente com um tipo especial, como uma 'HandleableException' personalizada, basta ser verifique se a propriedade InnerException também está definida!


Sim, essa é uma daquelas perguntas em que você precisa escrever rápido. ;)
Robert Harvey

0

A diferença mais importante é que a segunda expressão apaga o tipo de exceção. E o tipo de exceção desempenha um papel vital na captura de exceções:

public void MyMethod ()
{
    // both can throw IOException
    try { foo(); } catch { throw; }
    try { bar(); } catch(E) {throw new Exception(E.message); }
}

(...)

try {
    MyMethod ();
} catch (IOException ex) {
    Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
    Console.WriteLine ("Other error");    // [2]
}

Se foo()jogar IOException, o [1]bloco catch irá capturar a exceção. Mas quando bar()jogado IOException, ele será convertido em Exceptionformiga comum e não será pego pelo [1]bloco catch.


0

throw ou throw ex, ambos são usados ​​para lançar ou repetir a exceção, quando você simplesmente registra as informações de erro e não deseja enviar nenhuma informação ao chamador, simplesmente registra o erro em catch and leave. Mas, caso você queira enviar algumas informações significativas sobre a exceção para o chamador, use throw ou throw ex. Agora, a diferença entre throw e throw ex é que throw preserva o rastreamento de pilha e outras informações, mas throw ex cria um novo objeto de exceção e, portanto, o rastreamento de pilha original é perdido. Portanto, quando devemos usar throw and throw e, ainda existem algumas situações em que você pode querer relançar uma exceção como redefinir as informações da pilha de chamadas. Por exemplo, se o método estiver em uma biblioteca e você deseja ocultar os detalhes da biblioteca do código de chamada, você não necessariamente deseja que a pilha de chamadas inclua informações sobre métodos privados na biblioteca. Nesse caso, você pode capturar exceções nos métodos públicos da biblioteca e, em seguida, repeti-las novamente para que a pilha de chamadas comece nesses métodos públicos.


0

Lançar; Redefina a exceção original e mantenha o tipo de exceção.

Lança nova exceção (); Redefina o tipo de exceção original e redefina o rastreamento da pilha de exceções

Jogue ex; Redefina o rastreamento da pilha de exceções e redefina o tipo de exceção


-1

Nenhuma das respostas aqui mostra a diferença, o que pode ser útil para as pessoas que lutam para entender a diferença. Considere este código de exemplo:

using System;
using System.Collections.Generic;

namespace ExceptionDemo
{
   class Program
   {
      static void Main(string[] args)
      {
         void fail()
         {
            (null as string).Trim();
         }

         void bareThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw;
            }
         }

         void rethrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw e;
            }
         }

         void innerThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw new Exception("outer", e);
            }
         }

         var cases = new Dictionary<string, Action>()
         {
            { "Bare Throw:", bareThrow },
            { "Rethrow", rethrow },
            { "Inner Throw", innerThrow }
         };

         foreach (var c in cases)
         {
            Console.WriteLine(c.Key);
            Console.WriteLine(new string('-', 40));
            try
            {
               c.Value();
            } catch (Exception e)
            {
               Console.WriteLine(e.ToString());
            }
         }
      }
   }
}

O que gera a seguinte saída:

Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
   --- End of inner exception stack trace ---
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

O lance simples, conforme indicado nas respostas anteriores, mostra claramente a linha de código original que falhou (linha 12) e os dois outros pontos ativos na pilha de chamadas quando a exceção ocorreu (linhas 19 e 64).

A saída do caso re-throw mostra por que é um problema. Quando a exceção é repetida novamente, a exceção não inclui as informações da pilha original. Observe que apenas othrow e (linha 35) e o ponto da pilha de chamadas mais externas (linha 64) estão incluídos. Seria difícil rastrear o método fail () como a fonte do problema se você lançar exceções dessa maneira.

O último caso (innerThrow) é mais elaborado e inclui mais informações do que qualquer um dos itens acima. Como instanciamos uma nova exceção, temos a chance de adicionar informações contextuais (a mensagem "externa", aqui, mas também podemos adicionar ao dicionário .Data da nova exceção), além de preservar todas as informações no original exceção (incluindo links de ajuda, dicionário de dados etc.).

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.