A diferença entre tentar / pegar / lançar e tentar / pegar (e) / lançar e

103

Qual é a diferença entre

try { }
catch
{ throw; }

e

try { }
catch(Exception e)
{ throw e;}

?

E quando devo usar um ou outro?

Karim
fonte

Respostas:

151

As construções

try { ... }
catch () { ... } /* You can even omit the () here */

try { ... }
catch (Exception e) { ... }

são semelhantes no sentido de que ambos capturarão todas as exceções lançadas dentro do trybloco (e, a menos que você esteja simplesmente usando isso para registrar as exceções, devem ser evitados ). Agora olhe para estes:

try { ... }
catch ()
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw e;
}

O primeiro e o segundo blocos try-catch são EXATAMENTE a mesma coisa, eles simplesmente relançam a exceção atual e essa exceção manterá sua "origem" e o rastreamento de pilha.

O terceiro bloco try-catch é diferente. Ao lançar a exceção, ele mudará a origem e o rastreamento de pilha, de modo que pareça que a exceção foi lançada a partir desse método, daquela mesma linha throw eno método que contém aquele bloco try-catch.

Qual você deve usar? Depende muito de cada caso.

Digamos que você tenha uma Personclasse com um .Save()método que o manterá em um banco de dados. Digamos que seu aplicativo execute o Person.Save()método em algum lugar. Se seu banco de dados se recusar a salvar a pessoa, .Save()lançará uma exceção. Você deve usar throwou throw eneste caso? Bem, isto depende.

O que eu prefiro é fazer:

try {
    /* ... */
    person.Save();
}
catch(DBException e) {
    throw new InvalidPersonException(
       "The person has an invalid state and could not be saved!",
       e);
}

Isso deve colocar o DBException como a "Exceção interna" da exceção mais recente sendo lançada. Portanto, ao inspecionar esta InvalidPersonException, o rastreamento da pilha conterá informações de volta ao método Save (que pode ser suficiente para resolver o problema), mas você ainda terá acesso à exceção original se precisar.

Como uma observação final, quando você está esperando uma exceção, você realmente deve capturar essa exceção específica, e não uma geral Exception, ou seja, se você está esperando uma InvalidPersonException, você deve preferir:

try { ... }
catch (InvalidPersonException e) { ... }

para

try { ... }
catch (Exception e) { ... }

Boa sorte!

Bruno reis
fonte
34

O primeiro preserva o rastreamento da pilha, enquanto o segundo o redefine. Isso significa que se você usar a segunda abordagem o rastreamento de pilha da exceção sempre começará a partir deste método e você perderá o rastreamento de exceção original, o que pode ser desastroso para alguém que lê os logs de exceção, pois ele nunca descobrirá a causa original da exceção .

A segunda abordagem pode ser útil quando você deseja adicionar informações adicionais ao rastreamento de pilha, mas é usada assim:

try
{
    // do something
}
catch (Exception ex)
{
    throw new Exception("Additional information...", ex);
}

Há uma postagem no blog discutindo as diferenças.

Darin Dimitrov
fonte
Bem, isso é ótimo para saber!
Myles
então por que usar o segundo então? é melhor usar apenas o primeiro?
Karim
1
O segundo é útil quando você precisa verificar exceções específicas - OutOfRangeException vem à mente - ou precisa registrar a mensagem, etc. O primeiro parece ser um manipulador de exceção curinga semelhante a tentar {} catch (...) {} em c ++.
3Dave
1
David, isso só se aplica à parte catch (Exceção e) . E que é separado do throwvs throw e.
Henk Holterman
6

Você deveria usar

try { }
catch(Exception e)
{ throw }

se você quiser fazer algo com a exceção antes de lançá-la novamente (log, por exemplo). O lançamento solitário preserva o rastreamento da pilha.

Otávio Décio
fonte
e o que acontecerá se eu substituir o "lance" aqui por um "lance e"?
Karim
5

A diferença entre um catch sem parâmetros e um catch(Exception e)é que você obtém uma referência para a exceção. A partir da versão 2 do framework, as exceções não gerenciadas são agrupadas em uma exceção gerenciada, portanto, a exceção sem parâmetros não é mais útil para nada.

A diferença entre throw;e throw e;é que o primeiro é usado para relançar exceções e o segundo é usado para lançar uma exceção recém-criada. Se você usar o segundo para relançar uma exceção, ele a tratará como uma nova exceção e substituirá todas as informações da pilha de onde foi originalmente lançada.

Portanto, você não deve usar nenhuma das alternativas na pergunta. Você não deve usar o catch sem parâmetros e deve usar throw;para relançar uma exceção.

Além disso, na maioria dos casos, você deve usar uma classe de exceção mais específica do que a classe base para todas as exceções. Você só deve capturar as exceções que prevê.

try {
   ...
} catch (IOException e) {
   ...
   throw;
}

Se você quiser adicionar qualquer informação ao relançar a exceção, crie uma nova exceção com a exceção original como uma exceção interna para preservar todas as informações:

try {
   ...
} catch (IOException e) {
   ...
   throw new ApplicationException("Some informative error message", e);
}
Guffa
fonte