Vi pessoas dizerem que é uma má forma usar catch sem argumentos, especialmente se esse catch não fizer nada:
StreamReader reader=new StreamReader("myfile.txt");
try
{
int i = 5 / 0;
}
catch // No args, so it will catch any exception
{}
reader.Close();
No entanto, isso é considerado uma boa forma:
StreamReader reader=new StreamReader("myfile.txt");
try
{
int i = 5 / 0;
}
finally // Will execute despite any exception
{
reader.Close();
}
Até onde eu sei, a única diferença entre colocar o código de limpeza em um bloco finalmente e colocar o código de limpeza após os blocos try..catch é se você tiver instruções de retorno no bloco try (nesse caso, o código de limpeza finalmente será executar, mas o código após o try..catch não será).
Caso contrário, o que há de tão especial finalmente?
c#
.net
exception-handling
try-catch
try-catch-finally
mbeckish
fonte
fonte
Respostas:
A grande diferença é que isso
try...catch
irá engolir a exceção, ocultando o fato de que ocorreu um erro.try..finally
executará seu código de limpeza e a exceção continuará, sendo manipulada por algo que sabe o que fazer com ele.fonte
"Finalmente" é uma declaração de "Algo que você deve sempre fazer para garantir que o estado do programa esteja correto". Como tal, é sempre bom ter um, se houver possibilidade de que exceções possam prejudicar o estado do programa. O compilador também se esforça muito para garantir que seu código Finalmente seja executado.
"Catch" é uma declaração de "Eu posso me recuperar dessa exceção". Você só deve se recuperar das exceções que realmente pode corrigir - catch sem argumentos diz "Ei, eu posso me recuperar de qualquer coisa!", O que quase sempre é falso.
Se fosse possível recuperar de todas as exceções, seria realmente uma queixa semântica sobre o que você está declarando sua intenção. No entanto, não é, e quase certamente os quadros acima dos seus estarão mais bem equipados para lidar com certas exceções. Como tal, use finalmente, faça com que seu código de limpeza seja executado gratuitamente, mas ainda assim permita que manipuladores mais experientes lidem com o problema.
fonte
Porque quando essa única linha lança uma exceção, você não saberia.
Com o primeiro bloco de código, a exceção será simplesmente absorvida , o programa continuará sendo executado mesmo quando o estado do programa estiver errado.
Com o segundo bloco, a exceção será lançada e borbulha , mas o
reader.Close()
ainda é garantido para ser executado.Se uma exceção não for esperada, não coloque um bloco try..catch, pois será difícil depurar mais tarde quando o programa estiver em um estado ruim e você não tiver uma idéia do porquê.
fonte
Finalmente é executado, não importa o quê. Portanto, se seu bloco try foi bem-sucedido, ele será executado; se seu bloco try falhar, ele executará o bloco catch e, em seguida, o bloco final.
Além disso, é melhor tentar usar a seguinte construção:
Como a instrução using é automaticamente envolvida em uma tentativa / finalmente e o fluxo será fechado automaticamente. (Você precisará colocar uma tentativa / captura em torno da instrução using se realmente deseja capturar a exceção).
fonte
Embora os dois blocos de código a seguir sejam equivalentes, eles não são iguais.
finalmente os blocos são especiais. O CLR reconhece e trata o código dentro de um bloco final separadamente dos blocos de captura, e o CLR se esforça bastante para garantir que um bloco finalmente seja executado sempre. Não é apenas o açúcar sintático do compilador.
fonte
Concordo com o que parece ser o consenso aqui - uma 'captura' vazia é ruim porque mascara qualquer exceção que possa ter ocorrido no bloco try.
Além disso, do ponto de vista da legibilidade, quando vejo um bloco 'try', presumo que haverá uma instrução 'catch' correspondente. Se você estiver usando apenas uma 'tentativa' para garantir que os recursos sejam desalocados no bloco 'finalmente', considere a instrução 'using' :
Você pode usar a instrução 'using' com qualquer objeto que implemente IDisposable. O método dispose () do objeto é chamado automaticamente no final do bloco.
fonte
Use
Try..Catch..Finally
, se o seu método souber como lidar com a exceção localmente. A exceção ocorre em Try, Handled in Catch e depois que a limpeza é feita em Finalmente.Caso seu método não saiba como lidar com a exceção, mas precise de uma limpeza após a ocorrência, use
Try..Finally
Por isso, a exceção é propagada para os métodos de chamada e manipulada se houver alguma instrução Catch adequada nos métodos de chamada. Se não houver manipuladores de exceção no método atual ou em qualquer um dos métodos de chamada, o aplicativo trava.
Por
Try..Finally
isso, é garantido que a limpeza local seja feita antes de propagar a exceção para os métodos de chamada.fonte
O bloco try..finally ainda lançará quaisquer exceções levantadas. Tudo o
finally
que é necessário é garantir que o código de limpeza seja executado antes que a exceção seja lançada.O try..catch com uma captura vazia consumirá completamente qualquer exceção e oculta o fato de que aconteceu. O leitor estará fechado, mas não há como saber se a coisa certa aconteceu. E se sua intenção fosse gravar i no arquivo? Nesse caso, você não chegará a essa parte do código e o myfile.txt estará vazio. Todos os métodos a jusante lidam com isso corretamente? Quando vir o arquivo vazio, você poderá adivinhar corretamente que ele está vazio porque uma exceção foi lançada? É melhor lançar a exceção e informar que você está fazendo algo errado.
Outro motivo é o try..catch feito dessa maneira está completamente incorreto. O que você está dizendo ao fazer isso é: "Não importa o que aconteça, eu posso lidar com isso". Que tal
StackOverflowException
, você pode limpar depois disso? Que talOutOfMemoryException
? Em geral, você deve lidar apenas com as exceções que espera e sabe como lidar.fonte
Se você não souber que tipo de exceção capturar ou o que fazer com ele, não há sentido em ter uma instrução catch. Você deve deixar para um chamador superior que possa ter mais informações sobre a situação para saber o que fazer.
Você ainda deve ter uma declaração finalmente, caso haja uma exceção, para poder limpar os recursos antes que essa exceção seja lançada para o chamador.
fonte
Do ponto de vista da legibilidade, é mais explícito dizer aos futuros leitores de código "que esse material aqui é importante, precisa ser feito, aconteça o que acontecer". Isso é bom.
Além disso, as declarações de captura vazias tendem a ter um certo "cheiro" para elas. Eles podem ser um sinal de que os desenvolvedores não estão pensando nas várias exceções que podem ocorrer e em como lidar com elas.
fonte
Finalmente, é opcional - não há razão para ter um bloco "Finalmente" se não houver recursos para limpar.
fonte
Retirado de: aqui
Gerar e capturar exceções não deve ocorrer rotineiramente como parte da execução bem-sucedida de um método. Ao desenvolver bibliotecas de classes, o código do cliente deve ter a oportunidade de testar uma condição de erro antes de executar uma operação que possa resultar em uma exceção. Por exemplo, System.IO.FileStream fornece uma propriedade CanRead que pode ser verificada antes de chamar o método Read, evitando que uma possível exceção seja gerada, conforme ilustrado no seguinte trecho de código:
Dim str As Stream = GetStream () If (str.CanRead) Then 'código para ler o fluxo End If
A decisão de verificar o estado de um objeto antes de chamar um método específico que pode gerar uma exceção depende do estado esperado do objeto. Se um objeto FileStream for criado usando um caminho de arquivo que deveria existir e um construtor que retornasse um arquivo no modo de leitura, a verificação da propriedade CanRead não é necessária; a incapacidade de ler o FileStream seria uma violação do comportamento esperado das chamadas de método feitas e uma exceção deveria ser gerada. Por outro lado, se um método estiver documentado como retornando uma referência FileStream que pode ou não ser legível, é recomendável verificar a propriedade CanRead antes de tentar ler os dados.
Para ilustrar o impacto no desempenho que o uso de uma técnica de codificação "executar até exceção" pode causar, o desempenho de uma conversão, que lança uma InvalidCastException se a conversão falhar, é comparado ao C # como operador, que retorna nulos se uma conversão falhar. O desempenho das duas técnicas é idêntico para o caso em que a conversão é válida (consulte o Teste 8.05), mas para o caso em que a conversão é inválida e o uso de uma conversão causa uma exceção, o uso da conversão é 600 vezes mais lento que o uso do método como operador (consulte Teste 8.06). O impacto de alto desempenho da técnica de lançamento de exceção inclui o custo de alocar, lançar e capturar a exceção e o custo da coleta de lixo subsequente do objeto de exceção, o que significa que o impacto instantâneo de lançar uma exceção não é tão alto. Quanto mais exceções forem lançadas,
fonte
É uma prática ruim adicionar uma cláusula catch apenas para repetir a exceção.
fonte
Se você ler C # para programadores , entenderá que o bloco final foi projetado para otimizar um aplicativo e impedir o vazamento de memória.
Por exemplo, quando você abre uma conexão de arquivo ou banco de dados, sua máquina alocará memória para atender a essa transação, e essa memória será mantida não, a menos que o comando descartado ou próximo tenha sido executado. mas se durante a transação ocorreu um erro, o comando de continuação será encerrado, a menos que esteja dentro do
try.. finally..
bloco.catch
era diferentefinally
no sentido de que catch foi projetado para permitir que você lida com / gerencie ou interprete o erro. Pense nisso como uma pessoa que diz "ei, peguei alguns bandidos, o que você quer que eu faça com eles?" whilefinally
foi projetado para garantir que seus recursos foram colocados corretamente. Pense em alguém que, havendo ou não alguns bandidos, ele garantirá que sua propriedade ainda esteja segura.E você deve permitir que esses dois trabalhem juntos para sempre.
por exemplo:
fonte
Com finalmente, você pode limpar os recursos, mesmo que sua declaração de captura gere a exceção até o programa de chamada. Com o seu exemplo contendo a instrução catch vazia, há pouca diferença. No entanto, se na sua captura, você faz algum processamento e lança o erro, ou mesmo nem sequer tem uma captura, o finalmente ainda será executado.
fonte
Bem, por um lado, é uma prática ruim capturar exceções que você não se importa em lidar. Confira o Capítulo 5 sobre o desempenho do .Net em Melhorando o desempenho e a escalabilidade do aplicativo .NET . Nota: você provavelmente deveria estar carregando o fluxo dentro do bloco try, dessa forma, poderá capturar a exceção pertinente se falhar. Criar o fluxo fora do bloco try anula seu objetivo.
fonte
Entre provavelmente muitos motivos, as exceções são muito lentas para executar. Você pode facilmente prejudicar seus tempos de execução, se isso acontecer muito.
fonte
O problema com os blocos try / catch que capturam todas as exceções é que seu programa está agora em um estado indeterminado se ocorrer uma exceção desconhecida. Isso é totalmente contrário à regra de falha rápida - você não deseja que seu programa continue se ocorrer uma exceção. A tentativa / captura acima até capturaria OutOfMemoryExceptions, mas esse é definitivamente um estado em que seu programa não será executado.
Os blocos try / finalmente permitem executar o código de limpeza enquanto ainda falham rapidamente. Na maioria das circunstâncias, você deseja capturar todas as exceções no nível global, para que você possa registrá-las e sair.
fonte
A diferença efetiva entre seus exemplos é insignificante, desde que nenhuma exceção seja lançada.
Se, no entanto, uma exceção for lançada enquanto estiver na cláusula 'try', o primeiro exemplo a engolirá completamente. O segundo exemplo aumentará a exceção para a próxima etapa da pilha de chamadas, portanto a diferença nos exemplos declarados é que um oculta completamente todas as exceções (primeiro exemplo) e o outro (segundo exemplo) retém as informações de exceção para possível tratamento posterior enquanto ainda executando o conteúdo na cláusula 'finally'.
Se, por exemplo, você colocasse o código na cláusula 'catch' do primeiro exemplo que gerou uma exceção (aquela que foi inicialmente levantada ou uma nova), o código de limpeza do leitor nunca seria executado. Finalmente, executa independentemente do que acontece na cláusula 'catch'.
Portanto, a principal diferença entre 'catch' e 'finalmente' é que o conteúdo do bloco 'finally' (com algumas raras exceções) pode ser considerado com garantia de execução, mesmo diante de uma exceção inesperada, enquanto qualquer código a seguir uma cláusula de 'captura' (mas fora de uma cláusula 'finalmente') não teria essa garantia.
Aliás, Stream e StreamReader implementam IDisposable e podem ser agrupados em um bloco 'using'. Blocos 'Using' são o equivalente semântico de try / finalmente (sem 'catch'); portanto, seu exemplo pode ser expresso de forma mais concisa como:
... que fechará e descartará a instância StreamReader quando ela estiver fora do escopo. Espero que isto ajude.
fonte
tentar {…} pegar {} nem sempre é ruim. Não é um padrão comum, mas costumo usá-lo quando preciso encerrar recursos, não importa como, como fechar um (possivelmente) soquete aberto no final de um encadeamento.
fonte