No momento, estou escrevendo meu primeiro aplicativo Windows Forms. Eu li alguns livros sobre C # agora, então tenho um entendimento relativamente bom de quais recursos de linguagem C # tem para lidar com exceções. Eles são todos bastante teóricos, no entanto, o que eu ainda não entendi é como traduzir os conceitos básicos em um bom modelo de tratamento de exceções em meu aplicativo.
Alguém gostaria de compartilhar alguma pérola de sabedoria sobre o assunto? Publique quaisquer erros comuns que você viu iniciantes como eu cometer e qualquer conselho geral sobre como lidar com exceções de uma forma que deixe meu aplicativo mais estável e robusto.
As principais coisas que estou tentando resolver são:
- Quando devo relançar uma exceção?
- Devo tentar ter um mecanismo central de tratamento de erros de algum tipo?
- O tratamento de exceções que podem ser lançadas prejudica o desempenho em comparação com o teste preventivo, como a existência de um arquivo no disco?
- Todos os códigos executáveis devem ser colocados em blocos try-catch-finally?
- Há algum momento em que um bloco catch vazio pode ser aceitável?
Todos os conselhos recebidos com gratidão!
fonte
ApplicationException
para aqueles casos de falha que o aplicativo deve ser razoavelmente capaz de diferenciar e tratar.Há um excelente artigo CodeProject de código aqui . Aqui estão alguns destaques:
fonte
Observe que o Windows Forms tem seu próprio mecanismo de tratamento de exceções. Se um botão no formulário for clicado e seu manipulador lançar uma exceção que não foi capturada no manipulador, o Windows Forms exibirá sua própria caixa de diálogo de exceção não tratada.
Para evitar que o Unhandled Exception Dialog seja exibido e capturar tais exceções para registro e / ou para fornecer seu próprio diálogo de erro, você pode anexar ao evento Application.ThreadException antes da chamada para Application.Run () em seu método Main ().
fonte
Todos os conselhos postados aqui até agora são bons e valem a pena ser ouvidos.
Uma coisa que eu gostaria de expandir é a sua pergunta "O tratamento de exceções que podem ser lançadas prejudica o desempenho em comparação com o teste preventivo, como a existência de um arquivo no disco?"
A regra ingênua é "os blocos try / catch são caros." Isso não é verdade. Tentar não é caro. É a captura, onde o sistema tem que criar um objeto Exception e carregá-lo com o rastreamento de pilha, que é caro. Existem muitos casos em que a exceção é, bem, excepcional o suficiente para que seja perfeitamente possível agrupar o código em um bloco try / catch.
Por exemplo, se você estiver preenchendo um Dicionário, este:
geralmente é mais rápido do que fazer isso:
para cada item que você está adicionando, porque a exceção só é lançada quando você adiciona uma chave duplicada. (As consultas agregadas do LINQ fazem isso.)
No exemplo que você deu, eu usaria try / catch quase sem pensar. Primeiro, só porque o arquivo existe quando você o verifica, não significa que ele existirá quando você o abrir, portanto, você realmente deve lidar com a exceção de qualquer maneira.
Em segundo lugar, e acho mais importante, a menos que seu a) seu processo esteja abrindo milhares de arquivos eb) as chances de um arquivo que está tentando abrir não existir não sejam trivialmente baixas, o impacto no desempenho de criar a exceção não é algo que você ' nunca vai notar. De modo geral, quando seu programa está tentando abrir um arquivo, ele está apenas tentando abrir um arquivo. Esse é um caso em que escrever um código mais seguro quase certamente será melhor do que escrever o código mais rápido possível.
fonte
dict[key] = value
que deve ser tão rápido, senão mais rápido ..Aqui estão algumas diretrizes que sigo
Fail-Fast: Esta é mais uma diretriz geradora de exceções. Para cada suposição que você fizer e cada parâmetro que estiver inserindo em uma função, faça uma verificação para ter certeza de que está começando com os dados corretos e que as suposições que você está fazendo estão corretas. As verificações típicas incluem argumento não nulo, argumento no intervalo esperado etc.
Ao relançar, preservar o rastreamento de pilha - Isso simplesmente se traduz em lançar ao relançar em vez de lançar um novo Exception (). Alternativamente, se você sentir que pode adicionar mais informações, agrupe a exceção original como uma exceção interna. Mas se você estiver capturando uma exceção apenas para registrá-la, use definitivamente throw;
Não pegue exceções que você não pode controlar, então não se preocupe com coisas como OutOfMemoryException porque se elas ocorrerem você não poderá fazer muito de qualquer maneira.
Enganche manipuladores de exceção globais e certifique-se de registrar o máximo de informações possível. Para winforms, conecte os eventos de exceção não tratada de appdomain e thread.
O desempenho só deve ser levado em consideração quando você analisa o código e vê que ele está causando um afunilamento de desempenho; por padrão, otimize para legibilidade e design. Então, sobre sua pergunta original sobre a verificação de existência do arquivo, eu diria que depende, se você pode fazer algo sobre o arquivo não estar lá, então sim, faça essa verificação, caso contrário, se tudo o que você vai fazer é lançar uma exceção se o arquivo não há então eu não vejo o ponto.
Definitivamente, há momentos em que blocos catch vazios são necessários. Acho que as pessoas que dizem o contrário não trabalharam em bases de código que evoluíram ao longo de várias versões. Mas eles devem ser comentados e revisados para garantir que sejam realmente necessários. O exemplo mais típico são os desenvolvedores usando try / catch para converter string em inteiro em vez de usar ParseInt ().
Se você espera que o chamador de seu código seja capaz de lidar com as condições de erro, crie exceções personalizadas que detalhem qual é a situação inesperada e forneçam informações relevantes. Caso contrário, apenas se atenha aos tipos de exceção embutidos tanto quanto possível.
fonte
Application.ThreadException
evento quando referenciou o evento "thread não tratada de exceção"?Gosto da filosofia de não pegar nada que não pretendo manipular, seja o que for que o manuseio signifique em meu contexto particular.
Eu odeio quando vejo códigos como:
Eu tenho visto isso de vez em quando e é muito difícil encontrar problemas quando alguém 'come' as exceções. Um colega de trabalho que tive faz isso e tende a contribuir para um fluxo constante de problemas.
Eu lanço novamente se há algo que minha classe particular precisa fazer em resposta a uma exceção, mas o problema precisa ser borbulhado para o método chamado onde isso aconteceu.
Acho que o código deve ser escrito de forma proativa e que as exceções devem ser para situações excepcionais, não para evitar o teste de condições.
fonte
Estou saindo, mas apresentarei uma breve descrição de onde usar o tratamento de exceções. Tentarei abordar seus outros pontos quando retornar :)
* Dentro do razoável. Não há necessidade de verificar se, digamos, um raio cósmico atingiu seus dados causando a inversão de alguns bits. Entender o que é "razoável" é uma habilidade adquirida por um engenheiro. É difícil quantificar, mas fácil de intuir. Ou seja, posso explicar facilmente por que uso um try / catch em qualquer instância particular, embora seja difícil imbuir outro com esse mesmo conhecimento.
Eu, por exemplo, tendo a evitar arquiteturas fortemente baseadas em exceções. try / catch não tem um acerto de desempenho como tal, o acerto surge quando a exceção é lançada e o código pode ter que subir vários níveis da pilha de chamadas antes que algo o trate.
fonte
A regra de ouro que tentei seguir é tratar a exceção o mais próximo possível da fonte.
Se você precisar relançar uma exceção, tente adicionar a ela, relançar uma FileNotFoundException não ajuda muito, mas lançar uma ConfigurationFileNotFoundException permitirá que ela seja capturada e aplicada em algum lugar na cadeia.
Outra regra que tento seguir é não usar try / catch como uma forma de fluxo de programa, então eu verifico arquivos / conexões, garanto que os objetos foram iniciados, etc., antes de usá-los. Try / catch deve ser para exceções, coisas que você não pode controlar.
Quanto a um bloco catch vazio, se você estiver fazendo algo importante no código que gerou a exceção, você deve relançar a exceção no mínimo. Se não houver consequências para o código que gerou a exceção não funcionar, por que você o escreveu em primeiro lugar?
fonte
você pode interceptar o evento ThreadException.
Selecione um projeto de aplicativo do Windows no Solution Explorer.
Abra o arquivo Program.cs gerado clicando duas vezes nele.
Adicione a seguinte linha de código ao topo do arquivo de código:
No método Main (), adicione o seguinte como a primeira linha do método:
Adicione o seguinte abaixo do método Main ():
Adicione código para tratar a exceção não tratada dentro do manipulador de eventos. Qualquer exceção que não seja tratada em nenhum outro lugar do aplicativo é tratada pelo código acima. Mais comumente, esse código deve registrar o erro e exibir uma mensagem ao usuário.
refrence: https://blogs.msmvps.com/deborahk/global-exception-handler-winforms/
fonte
As exceções são caras, mas necessárias. Você não precisa envolver tudo em um try catch, mas precisa garantir que as exceções sempre sejam detectadas eventualmente. Muito disso dependerá do seu projeto.
Não re-lance se permitir que a exceção aumente servirá tão bem. Nunca deixe os erros passarem despercebidos.
exemplo:
Se DoStuff der errado, você vai querer que ele saia de qualquer maneira. A exceção será lançada para principal e você verá a seqüência de eventos no rastreamento de pilha de ex.
fonte
Em todos os lugares, mas métodos de usuário final ... como manipuladores de clique de botão
Eu escrevo um arquivo de log ... muito fácil para um aplicativo WinForm
Não tenho certeza sobre isso, mas acredito que seja uma boa prática para as exceções ... Quer dizer, você pode perguntar se um arquivo existe e se ele não lança um FileNotFoundException
sim
Sim, digamos que você queira mostrar uma data, mas você não tem ideia de como essa data foi armazenada (dd / mm / aaaa, mm / dd / aaaa, etc.), tente tp analisá-la, mas se falhar, continue. . se for irrelevante para você ... Eu diria que sim, há
fonte
A única coisa que aprendi muito rapidamente foi encerrar absolutamente cada pedaço de código que interage com qualquer coisa fora do fluxo do meu programa (isto é, sistema de arquivos, chamadas de banco de dados, entrada do usuário) com blocos try-catch. O try-catch pode causar um impacto no desempenho, mas geralmente nesses lugares do seu código isso não será perceptível e se pagará com segurança. realmente precisa postar uma mensagem, mas se você não pegá-la, ela lançará um erro de exceção não tratada para o usuário.
Eu usei catch-blocks vazios em lugares onde o usuário pode fazer algo que não é realmente "incorreto", mas pode lançar uma exceção ... um exemplo que vem à mente é em um GridView se o usuário DoubleCLicks o marcador cinza célula no canto superior esquerdo irá disparar o evento CellDoubleClick, mas a célula não pertence a uma linha. Nesse caso, você não
fonte
Ao relançar uma exceção, a palavra-chave é lançada por ela mesma. Isso lançará a exceção capturada e ainda será capaz de usar o rastreamento de pilha para ver de onde veio.
fazer isso fará com que o rastreamento da pilha termine na instrução catch. (evite isso)
fonte
Em minha experiência, achei adequado capturar exceções quando sei que vou criá-las. Por exemplo, quando estou em um aplicativo da web e estou fazendo um Response.Redirect, sei que receberei um System.ThreadAbortException. Uma vez que é intencional, eu apenas tenho uma captura para o tipo específico e apenas engulo.
fonte
Eu concordo profundamente com a regra de:
O motivo é que:
ForceAssert.AlwaysAssert é minha forma pessoal de Trace.Assert, independentemente de a macro DEBUG / TRACE estar definida ou não.
O ciclo de desenvolvimento talvez: notei a caixa de diálogo Assert feia ou outra pessoa reclamará comigo sobre isso, então eu volto para o código e descubro o motivo para levantar a exceção e decido como processá-la.
Desta forma posso escrever o MEU código em um curto espaço de tempo e me proteger de domínio desconhecido, mas sempre sendo notado se coisas anormais acontecerem, desta forma o sistema fica seguro e com mais segurança.
Sei que muitos de vocês não concordarão comigo porque um desenvolvedor deve saber todos os detalhes de seu código, francamente, também sou um purista nos velhos tempos. Mas hoje em dia aprendi que a política acima é mais pragmática.
Para o código WinForms, uma regra de ouro que sempre obedeço é:
isso protegerá sua interface do usuário sendo sempre utilizável.
Para o impacto no desempenho, a penalidade no desempenho só acontece quando o código atinge o catch, a execução do código try sem a exceção real gerada não tem efeito significativo.
A exceção deve acontecer com pouca chance, caso contrário, não são exceções.
fonte
Você tem que pensar no usuário. O travamento do aplicativo é o últimocoisa que o usuário deseja. Portanto, qualquer operação que possa falhar deve ter um bloco try catch no nível da interface do usuário. Não é necessário usar try catch em todos os métodos, mas sempre que o usuário faz algo, ele deve ser capaz de lidar com exceções genéricas. Isso de forma alguma o libera de verificar tudo para evitar exceções no primeiro caso, mas não há aplicativo complexo sem bugs e o sistema operacional pode facilmente adicionar problemas inesperados, portanto, você deve antecipar o inesperado e certificar-se de que um usuário deseja usar um operação não haverá perda de dados porque o aplicativo trava. Não há necessidade de deixar seu aplicativo travar, se você pegar exceções, ele nunca estará em um estado indeterminado e o usuário SEMPRE será incomodado por um travamento. Mesmo se a exceção estiver no nível mais alto, não travar significa que o usuário pode reproduzir rapidamente a exceção ou pelo menos registrar a mensagem de erro e, portanto, ajudá-lo muito a corrigir o problema. Certamente muito mais do que receber uma simples mensagem de erro e ver apenas a caixa de diálogo de erro do Windows ou algo parecido.
É por isso que você NUNCA deve ser convencido e pensar que seu aplicativo não tem bugs, isso não é garantido. E é um esforço muito pequeno envolver alguns blocos try catch sobre o código apropriado e mostrar uma mensagem de erro / registrar o erro.
Como um usuário, eu certamente fico seriamente irritado sempre que um aplicativo de escritório ou navegador travar. Se a exceção for tão alta que o aplicativo não possa continuar, é melhor exibir essa mensagem e dizer ao usuário o que fazer (reiniciar, corrigir algumas configurações do sistema operacional, relatar o bug, etc.) do que simplesmente travar e pronto.
fonte