Pergunta

Vi pessoas dizerem que é uma forma ruim usar a captura sem argumentos, especialmente se essa captura 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 a tentativa. Corra, mas codifique após a tentativa ... o tiro não será).

Caso contrário, o que há de tão especial finalmente?

Foi útil?

Solução

A grande diferença é que try...catch engolirá a exceção, escondendo o fato de que ocorreu um erro. try..finally Executará seu código de limpeza e, em seguida, a exceção continuará, a ser tratada por algo que sabe o que fazer com ele.

Outras dicas

"Finalmente" é uma declaração de "algo que você deve sempre fazer para garantir que o estado do programa seja sensato". Como tal, é sempre uma boa forma ter uma, se houver alguma possibilidade de que exceções possam descartar 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 de exceções que realmente pode corrigir - capturar sem argumentos diz: "Ei, eu posso me recuperar de qualquer coisa!", O que é quase sempre falso.

Se Foi possível se recuperar de todas as exceções, então seria realmente uma briga semântica, sobre o que você está declarando sua intenção de ser. No entanto, não é, e quase certamente os quadros acima do seu estarão melhor equipados para lidar com certas exceções. Como tal, use finalmente, faça o seu código de limpeza executado gratuitamente, mas ainda deixe que os manipuladores mais conhecidos lidam com o problema.

Porque quando essa única linha joga uma exceção, você não saberia.

Com o primeiro bloco de código, a exceção será simplesmente absorvido, o programa continuará sendo executado mesmo quando o estado do programa estiver errado.

Com o segundo bloco, a exceção será jogado e borbulha mas a reader.Close() ainda é garantido para correr.

Se uma exceção não for esperada, não experimente o bloco, é difícil depurar mais tarde, quando o programa entrou em um estado ruim e você não tiver uma idéia do porquê.

Finalmente é executado, não importa o quê. Portanto, se o seu bloco de tentativa foi bem -sucedido, ele será executado, se o seu bloco de tentativa falhar, ele executará o bloco de captura e o bloco finalmente.

Além disso, é melhor tentar usar o seguinte construto:

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

Como a instrução de uso é embrulhada automaticamente em uma tentativa / finalmente e o fluxo será fechado automaticamente. (Você precisará experimentar / pegar a instrução usando se quiser pegar a exceção).

Embora os 2 blocos de código a seguir sejam equivalentes, eles não são iguais.

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. 'Finalmente' é um código de revelação da intenção. Você declara ao compilador e a outros programadores que esse código precisa ser executado, não importa o quê.
  2. Se você tiver vários blocos de captura e o código de limpeza, finalmente precisa. Sem finalmente, você estaria duplicando seu código de limpeza em cada bloco de captura. (Princípio seco)

Finalmente, os blocos são especiais. O CLR reconhece e trata o código com um bloco finalmente separadamente dos blocos de captura, e o CLR se esforça para garantir que um bloco finalmente seja sempre executado. Não é apenas açúcar sintático do compilador.

Concordo com o que parece ser o consenso aqui - uma 'captura' vazia é ruim porque mascara qualquer exceção que possa ter ocorrido no bloco de tentativas.

Além disso, do ponto de vista da legibilidade, quando eu vejo um bloco 'tente', presumo que haverá uma instrução 'Catch' correspondente. Se você estiver usando apenas um 'tente' para garantir que os recursos sejam desalocados no bloco 'finalmente', você pode considerar o Declaração 'usando' em vez de:

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

Você pode usar a instrução 'Usando' com qualquer objeto que implementa idispossáveis. O método de dispete () do objeto é chamado automaticamente no final do bloco.

A tentativa ... finalmente o bloco ainda lançará todas as exceções levantadas. Tudo finally O faz é garantir que o código de limpeza seja executado antes que a exceção seja lançada.

A tentativa ... Chegue com uma captura vazia consumirá completamente qualquer exceção e ocultará o fato de que isso aconteceu. O leitor estará fechado, mas não há como saber se a coisa correta aconteceu. E se sua intenção fosse escrever eu para o arquivo? Nesse caso, você não chegará a essa parte do código e myfile.txt estará vazio. Todos os métodos a jusante lidam com isso corretamente? Quando você vê o arquivo vazio, poderá adivinhar corretamente que está vazio porque uma exceção foi lançada? Melhor fazer a exceção e saber que você está fazendo algo errado.

Outro motivo é a tentativa ... A escape assim é completamente incorreta. O que você está dizendo ao fazer isso é: "Não importa o que aconteça, eu posso lidar com isso". A respeito StackOverflowException, você pode limpar depois disso? A respeito OutOfMemoryException? Em geral, você deve lidar apenas com exceções que espera e sabe como lidar.

Usar Try..Catch..Finally, se o seu método souber como lidar com a exceção localmente. A exceção ocorre na tentativa, tratada em captura e depois que a limpeza é feita finalmente.

Caso seu método não saiba como lidar com a exceção, mas precisa de uma limpeza depois de ocorrer, use Try..Finally

Por isso, a exceção é propagada aos métodos de chamada e tratada se houver alguma declaração de captura adequada nos métodos de chamada. Se não houver manipuladores de exceção no método atual ou qualquer um dos métodos de chamada, o aplicativo trava.

Por Try..Finally É garantido que a limpeza local seja feita antes de propagar a exceção aos métodos de chamada.

Se você não sabe que tipo de exceção pegar ou o que fazer com ele, não faz sentido ter uma declaração de captura. Você deve deixá-lo para um chamador mais alto que pode ter mais informações sobre a situação para saber o que fazer.

Você ainda deve ter uma declaração finalmente lá caso haja uma exceção, para que você possa limpar os recursos antes que essa exceção seja lançada ao chamador.

De uma perspectiva de legibilidade, está mais explicitamente dizendo aos futuros leitores de código "essas coisas aqui são importantes, precisa ser feito, não importa o que aconteça". Isso é bom.

Além disso, declarações de captura vazias tendem a ter um certo "cheiro". Eles podem ser um sinal de que os desenvolvedores não estão pensando nas várias exceções que podem ocorrer e como lidar com eles.

Finalmente, é opcional - não há razão para ter um bloco "finalmente" se não houver recursos para limpar.

Tirado de: aqui

Aumentar e capturar exceções não devem 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 realizar uma operação que pode resultar em uma exceção levantada. Por exemplo, o System.io.FileStream fornece uma propriedade CanRead que pode ser verificada antes de chamar o método de leitura, impedindo uma exceção potencial sendo levantada, conforme ilustrado no seguinte snippet de código:

Dim str como stream = gettream () if (str.CanRead) então 'código para ler o final do fluxo se

A decisão de verificar o estado de um objeto antes de invocar um método específico que pode levantar uma exceção depende do estado esperado do objeto. Se um objeto FILESTREAM for criado usando um caminho de arquivo que deve existir e um construtor que deve retornar um arquivo no modo de leitura, verificação da propriedade CanRead não será 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 deve ser levantada. Por outro lado, se um método for documentado como devolução de uma referência de FileStream que pode ou não ser legível, é aconselhável verificar a propriedade CanRead antes de tentar ler dados.

Para ilustrar o impacto do desempenho que o uso de uma técnica de codificação "Executar até a exceção" pode causar, o desempenho de um elenco, que lança uma invalidcastException se o elenco falhar, é comparado ao C# como operador, que retorna nulos se um elenco falhar. O desempenho das duas técnicas é idêntico para o caso em que o elenco é válido (consulte o teste 8.05), mas para o caso em que o elenco é inválido, e o uso de um elenco causa uma exceção, o uso de um elenco é 600 vezes mais lento do que usar o como operador (consulte o teste 8.06). O impacto de alto desempenho da técnica de arremesso de exceção inclui o custo de alocar, jogar e capturar a exceção e o custo da coleta subsequente de lixo do objeto de exceção, o que significa que o impacto instantâneo de lançar uma exceção não é tão alto. À medida que mais exceções são lançadas, a coleta frequente de lixo se torna um problema, portanto, o impacto geral do uso frequente de uma técnica de codificação de arremesso de exceção será semelhante ao teste 8.05.

É uma prática ruim adicionar uma cláusula de captura apenas para repensar a exceção.

Se você ler C# para programadores Você entenderá que o bloco finalmente foi otimizar um aplicativo e impedir o vazamento de memória.

O CLR não elimina completamente vazamentos ... os vazamentos de memória podem ocorrer se o programa inadvertidamente mantiver referências a objetos indesejados

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 fechado tenha sido executado. Mas se durante a transação, ocorreu um erro, o comando de processo será encerrado, a menos que estivesse dentro do try.. finally.. quadra.

catch era diferente de finally No sentido de que o Catch foi o design para lhe dar lugar de lidar/gerenciar ou interpretar o erro. Pense nisso como uma pessoa que diz: "Ei, eu peguei alguns bandidos, o que você quer que eu faça com eles?" enquanto finally foi projetado para garantir que seus recursos fossem adequadamente colocados. Pense nisso de alguém que, se há ou não alguns bandidos, ele garantirá que sua propriedade ainda estivesse segura.

E você deve permitir que esses dois trabalhem juntos para sempre.

por exemplo:

try
{
  StreamReader reader=new  StreamReader("myfile.txt");
  //do other stuff
}
catch(Exception ex){
 // Create log, or show notification
 generic.Createlog("Error", ex.message);
}
finally   // Will execute despite any exception
{
  reader.Close();
}

Com finalmente, você pode limpar os recursos, mesmo que sua declaração de captura ligue a exceção ao programa de chamada. Com o seu exemplo contendo a declaração de captura vazia, há pouca diferença. No entanto, se estiver em sua captura, você faz algum processamento e lança o erro, ou mesmo nem sequer tira um problema, o finalmente ainda será executado.

Bem, por um lado, é uma prática ruim capturar exceções que você não se preocupa em lidar. Verificação de saída Capítulo 5 sobre o desempenho .NET a partir de Melhorando o desempenho e escalabilidade do aplicativo .NET. Nota lateral, você provavelmente deve estar carregando o fluxo dentro do bloco de tentativa, dessa maneira, você pode pegar a exceção pertinente se falhar. Criar o fluxo fora do bloqueio de tentativa derrota seu objetivo.

Entre provavelmente muitas razões, as exceções são muito lentas para executar. Você pode prejudicar facilmente seus tempos de execução se isso acontecer.

O problema com os blocos de tentativa/captura que capturam todas as exceções é que seu programa está agora em um estado indeterminado se ocorrer uma exceção desconhecida. Isso vai completamente contra a regra Fail Fast - você não deseja que seu programa continue se ocorrer uma exceção. A tentativa/captura acima, até capturaria o OfMemoryExceptions, mas esse é definitivamente um estado em que seu programa não será executado.

Tente/finalmente os blocos permitem executar o código de limpeza enquanto ainda falha rapidamente. Para a maioria das circunstâncias, você só deseja capturar todas as exceções em nível global, para poder registrá -las e sair.

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 o engolirá completamente. O segundo exemplo aumentará a exceção ao próximo passo na pilha de chamadas, de modo que a diferença nos exemplos declarados é que um obscurece completamente qualquer exceção (primeiro exemplo), e o outro (segundo exemplo) mantém informações de exceção para potencial manuseio posterior enquanto ainda executando o conteúdo na cláusula 'finalmente'.

Se, por exemplo, você colocasse código na cláusula 'Catch' do primeiro exemplo que lançou uma exceção (ou a que foi inicialmente criada ou uma nova), o código de limpeza do leitor nunca será executado. Finalmente executa sem considerar do que acontece na cláusula de 'captura'.

Portanto, a principal diferença entre 'captura' e 'finalmente' é que o conteúdo do bloco 'finalmente' (com algumas exceções raras) pode ser considerado garantido Executar, mesmo diante de uma exceção inesperada, enquanto qualquer código após uma cláusula 'captura' (mas fora de uma cláusula 'finalmente') não levaria essa garantia.

Aliás, o Stream e o StreamReader implementam o IDisposable e podem ser embrulhados em um bloco 'usando'. 'Usando' Os blocos são o equivalente semântico de tentar/finalmente (não 'capturar'), então seu exemplo pode ser mais expresso como:

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

... que fechará e descartará a instância do streamreader quando sair do escopo. Espero que isto ajude.

Tente {…} captura {} nem sempre é ruim. Não é um padrão comum, mas eu costumo usá -lo quando preciso desligar os recursos, não importa o que seja, como fechar um (possivelmente) soquetes abertos no final de um thread.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top