É desencorajado simplesmente pegar System.Exception
. Em vez disso, apenas as exceções "conhecidas" devem ser capturadas.
Agora, isso às vezes leva ao código repetitivo desnecessário, por exemplo:
try
{
WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
WebId = Guid.Empty;
}
catch (OverflowException)
{
WebId = Guid.Empty;
}
Eu me pergunto: existe uma maneira de capturar as duas exceções e ligar apenas WebId = Guid.Empty
uma vez?
O exemplo dado é bastante simples, pois é apenas um GUID
. Mas imagine o código em que você modifica um objeto várias vezes e, se uma das manipulações falhar da maneira esperada, você deseja "redefinir" o arquivo object
. No entanto, se houver uma exceção inesperada, ainda quero aumentar isso.
c#
.net
exception
exception-handling
Michael Stum
fonte
fonte
AggregateException
: Jogando um AggregateException no meu próprio códigoRespostas:
Pegar
System.Exception
e ligar os tiposfonte
Edição: Eu concordo com outras pessoas que estão dizendo que, a partir do C # 6.0, filtros de exceção agora são um caminho perfeitamente correto:
catch (Exception ex) when (ex is ... || ex is ... )
Só que eu ainda odeio o layout de uma linha longa e pessoalmente colocaria o código da seguinte maneira. Eu acho que isso é tão funcional quanto estético, pois acredito que melhora a compreensão. Alguns podem discordar:
ORIGINAL:
Eu sei que estou um pouco atrasado para a festa aqui, mas fumaça sagrada ...
Indo direto ao assunto, esse tipo de duplicata uma resposta anterior, mas se você realmente deseja executar uma ação comum para vários tipos de exceção e manter a coisa toda limpa e arrumada no escopo do método, por que não usar apenas um lambda / encerramento / função embutida para fazer algo como o seguinte? Quero dizer, as chances são muito boas de que você acabe percebendo que deseja apenas tornar esse fechamento um método separado que possa ser utilizado em todo o lugar. Mas será super fácil fazer isso sem alterar o restante do código estruturalmente. Direita?
Não posso deixar de me perguntar ( aviso: um pouco de ironia / sarcasmo pela frente) por que diabos vamos a todo esse esforço para basicamente substituir apenas o seguinte:
... com uma variação louca desse próximo cheiro de código, quero dizer exemplo, apenas para fingir que você está salvando algumas teclas.
Porque certamente não é automaticamente mais legível.
É verdade que deixei as três instâncias idênticas
/* write to a log, whatever... */ return;
do primeiro exemplo.Mas esse é o meu ponto. Vocês já ouviram falar de funções / métodos, certo? A sério. Escreva uma
ErrorHandler
função comum e, como, chame-a de cada bloco catch.Se você me perguntar, o segundo exemplo (com as palavras
if
-is
chave e ) é significativamente menos legível e, ao mesmo tempo, significativamente mais suscetível a erros durante a fase de manutenção do seu projeto.A fase de manutenção, para qualquer um que seja relativamente novo em programação, abrangerá 98,7% ou mais da vida útil geral do seu projeto, e o pobre coitado que faz a manutenção certamente será alguém que não seja você. E há uma chance muito boa de que eles gastem 50% do seu tempo no trabalho amaldiçoando seu nome.
E, é claro, o FxCop late para você e, portanto, você também deve adicionar um atributo ao seu código que seja exatamente o que está relacionado ao programa em execução, e existe apenas para solicitar ao FxCop que ignore um problema que, em 99,9% dos casos, é totalmente correto na sinalização. E, desculpe, posso estar enganado, mas esse atributo "ignorar" acaba realmente compilado no seu aplicativo?
Colocar o
if
teste inteiro em uma linha o tornaria mais legível? Acho que não. Quero dizer, eu tive outro programador argumentar veementemente, há muito tempo, que colocar mais código em uma linha o faria "correr mais rápido". Mas é claro que ele estava nojento. Tentando explicar a ele (com uma cara séria - o que foi desafiador) como o intérprete ou compilador dividiria essa longa linha em instruções discretas de uma instrução por linha - essencialmente idêntico ao resultado se ele tivesse ido em frente e apenas tornou o código legível em vez de tentar enganar o compilador - não teve nenhum efeito nele. Mas eu discordo.Quanto menos legível fica quando você adiciona mais três tipos de exceção, daqui a um mês ou dois? (Resposta: fica muito menos legível).
Um dos principais pontos, na verdade, é que a maior parte do ponto de formatar o código-fonte textual que todos nós estamos vendo todos os dias é torná-lo muito, muito óbvio para outros seres humanos o que realmente está acontecendo quando o código é executado. Como o compilador transforma o código-fonte em algo totalmente diferente e não se importava com o seu estilo de formatação de código. Então, tudo em uma linha também é uma droga também.
Apenas dizendo...
fonte
Exception
os se checar o tipo. Eu pensei que tinha limpado o código, mas algo me fez voltar à pergunta e, na verdade, li as outras respostas para a pergunta. Eu mastiguei por um tempo, mas tenho que concordar com você. É mais legível e fácil de usar usar uma função para secar seu código do que pegar tudo, verificar o tipo de comparação com uma lista, quebrar o código e jogar. Agradecemos por chegar atrasado e por fornecer uma opção alternativa e sã (IMO). +1.throw;
. Você teria que repetir essa linha de código em cada bloco catch (obviamente não é o fim do mundo, mas vale a pena mencionar, pois é um código que precisa ser repetido).throw();
declaração extra em cada bloco de captura é um pequeno preço a pagar, IMO, e ainda permite que você faça uma limpeza adicional específica do tipo de exceção, se necessário.Func<Exception, MyEnumType>
vez deAction<Exception>
. Isso éFunc<T, Result>
,Result
sendo o tipo de retorno.Como outros já apontaram, você pode ter uma
if
declaração dentro do seu bloco de captura para determinar o que está acontecendo. O C # 6 suporta filtros de exceção, portanto, o seguinte funcionará:O
MyFilter
método pode ser algo parecido com isto:Como alternativa, tudo isso pode ser feito em linha (o lado direito da instrução when só precisa ser uma expressão booleana).
Isso é diferente de usar uma
if
instrução de dentro docatch
bloco, o uso de filtros de exceção não desanuviará a pilha.Você pode baixar o Visual Studio 2015 para verificar isso.
Se você deseja continuar usando o Visual Studio 2013, pode instalar o seguinte pacote de nuget:
No momento da redação deste artigo, isso incluirá suporte ao C # 6.
fonte
Infelizmente, não em C #, pois você precisaria de um filtro de exceção para fazer isso e o C # não expõe esse recurso do MSIL. O VB.NET possui esse recurso, por exemplo,
O que você pode fazer é usar uma função anônima para encapsular seu código de erro e chamá-lo nesses blocos de captura específicos:
fonte
throw e;
Stacktrace destrói e callstack,throw;
destrói "apenas" callstack (renderização crash-dumps inútil!) Uma muito boa razão para usar nem se ele pode ser evitado!Por uma questão de integridade, desde o .NET 4.0, o código pode ser reescrito como:
O TryParse nunca lança exceções e retorna false se o formato estiver errado, configurando WebId como
Guid.Empty
.Desde o C # 7, você pode evitar a introdução de uma variável em uma linha separada:
Você também pode criar métodos para analisar as tuplas retornadas, que ainda não estão disponíveis no .NET Framework a partir da versão 4.6:
E use-os assim:
A próxima atualização inútil para essa resposta inútil ocorre quando a desconstrução de parâmetros externos é implementada em C # 12. :)
fonte
Guid.TryParse
nunca voltaGuid.Empty
. Se a sequência estiver em um formato incorreto, ela definirá oresult
parâmetro de saída comoGuid.Empty
, mas retornaráfalse
. Estou mencionando isso porque vi código que faz coisas no estilo deGuid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }
, que geralmente está errado ses
puder ser a representação de string deGuid.Empty
.if( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }
, o que não deixa ambiguidade como o exemplo quebrado, em que o valor de entrada pode realmente ser a representação de string de um Guid.Os filtros de exceção agora estão disponíveis no c # 6+. Você pode fazer
No C # 7.0+, você também pode combinar isso com a correspondência de padrões
fonte
Se você pode atualizar seu aplicativo para C # 6, terá sorte. A nova versão do C # implementou filtros de exceção. Então você pode escrever isso:
Algumas pessoas pensam que esse código é o mesmo que
Mas isso não. Na verdade, esse é o único novo recurso do C # 6 que não é possível emular nas versões anteriores. Primeiro, um re-lançamento significa mais sobrecarga do que pular a captura. Segundo, não é semanticamente equivalente. O novo recurso preserva a pilha intacta quando você está depurando seu código. Sem esse recurso, o despejo de memória é menos útil ou até inútil.
Veja uma discussão sobre isso no CodePlex . E um exemplo mostrando a diferença .
fonte
Se você não quiser usar uma
if
declaração dentro doscatch
escopos, emC# 6.0
que você pode usarException Filters
a sintaxe que já foi apoiada pelo CLR em versões previews mas existia apenas emVB.NET
/MSIL
:Esse código captura o
Exception
único quando é umInvalidDataException
ouArgumentNullException
.Na verdade, você pode colocar basicamente qualquer condição dentro dessa
when
cláusula:Observe que, ao contrário de uma
if
declaração dentro docatch
escopo da,Exception Filters
não pode ser lançadaExceptions
e, quando ocorrer , ou quando a condição não fortrue
, a próximacatch
condição será avaliada:Quando houver mais de um
true
Exception Filter
- o primeiro será aceito:E como você pode ver no
MSIL
código, o código não é traduzido paraif
instruções, mas paraFilters
, eExceptions
não pode ser jogado de dentro das áreas marcadas comFilter 1
e,Filter 2
mas o filtro que está lançandoException
falhará, também o último valor de comparação enviado à pilha antes doendfilter
comando determinará o sucesso / falha do filtro (oCatch 1
XORCatch 2
será executado de acordo):Além disso, especificamente,
Guid
tem oGuid.TryParse
métodofonte
Com o C # 7, a resposta de Michael Stum pode ser aprimorada, mantendo a legibilidade de uma instrução switch:
E com C # 8 como expressão de chave:
fonte
when
é muito mais elegante / apropriada do que um switch.catch
bloco diferente ou precisar convertê-lo de qualquer maneira. Na minha experiência, umthrow;
no seucatch
bloco é provavelmente um cheiro de código.A resposta aceita parece aceitável, exceto que CodeAnalysis / FxCop irá reclamar do fato de estar capturando um tipo de exceção geral.
Além disso, parece que o operador "é" pode prejudicar um pouco o desempenho.
CA1800: Não transmita desnecessariamente diz "considere testar o resultado do operador 'as'", mas se você fizer isso, estará escrevendo mais código do que se capturar cada exceção separadamente.
De qualquer forma, aqui está o que eu faria:
fonte
is
operador possa ter um leve impacto negativo no desempenho, também é verdade que um manipulador de exceções não é o lugar para se preocupar excessivamente com a otimização do desempenho. Se seu aplicativo está gastando tanto tempo em manipuladores de exceção que a otimização de desempenho faria uma diferença real no desempenho do aplicativo, existem outros problemas de código para examinar com atenção. Dito isso, ainda não gosto dessa solução porque você perde o rastreamento de pilha e porque a limpeza é contextualmente removida da instrução catch.is
operador diminui o desempenho é se você executar umaas
operação posteriormente (portanto, eles qualificam a regra desnecessariamente ). Se tudo o que você está fazendo é testar o elenco sem realmente precisar executá-lo, ois
operador é exatamente o que você deseja usar.no C # 6, a abordagem recomendada é usar filtros de exceção, aqui está um exemplo:
fonte
Esta é uma variante da resposta de Matt (acho que isso é um pouco mais limpo) ... use um método:
Quaisquer outras exceções serão lançadas e o código
WebId = Guid.Empty;
não será atingido. Se você não quiser que outras exceções travem seu programa, adicione-o APÓS as outras duas capturas:fonte
WebId = Guid.Emtpy
no caso em que nenhuma exceção foi lançada.return
a minha resposta. Obrigado pela contribuição.A resposta de Joseph Daigle é uma boa solução, mas achei a estrutura a seguir um pouco mais organizada e menos propensa a erros.
Existem algumas vantagens em inverter a expressão:
Pode até ser compactado em uma única linha (embora não seja muito bonita)
Editar: a filtragem de exceção no C # 6.0 tornará a sintaxe um pouco mais limpa e traz vários outros benefícios sobre qualquer solução atual. (principalmente deixando a pilha ilesa)
Aqui está como o mesmo problema ficaria usando a sintaxe do C # 6.0:
fonte
return
, embora inverter a condição também seja um pouco melhor.@Micheal
Versão ligeiramente revisada do seu código:
As comparações de strings são feias e lentas.
fonte
throw new FormatException();
comthrow new NewlyDerivedFromFormatException();
sem quebrar o código usando a biblioteca, e isso será uma realidade para todos os manipulação de exceção casos, excepto quando alguém usou==
em vez deis
(ou simplesmentecatch (FormatException)
).E se
fonte
fonte
Advertido e advertido: Mais um tipo de estilo funcional.
O conteúdo do link não responde diretamente à sua pergunta, mas é trivial estendê-la para que fique assim:
(Basicamente, forneça outra
Catch
sobrecarga vazia que retorne)A questão maior para isso é o porquê . Eu não acho que o custo supera o ganho aqui :)
fonte
Atualização 15/12/2015: consulte https://stackoverflow.com/a/22864936/1718702 para obter o C # 6. É um limpador e agora padrão no idioma.
Voltado para pessoas que desejam uma solução mais elegante para capturar uma vez e filtrar exceções, eu uso um método de extensão, conforme demonstrado abaixo.
Eu já tinha essa extensão na minha biblioteca, originalmente escrita para outros fins, mas funcionou perfeitamente para
type
verificar exceções. Além disso, parece que é mais limpo do que um monte de||
declarações. Além disso, diferentemente da resposta aceita, prefiro o tratamento explícito de exceções, para queex is ...
tenha um comportamento indesejável, pois as classes derivadas são atribuíveis aos tipos pai).Uso
Extensão IsAnyOf.cs (consulte o exemplo completo de tratamento de erros para dependências)
Exemplo completo de tratamento de erros (copiar e colar para o novo aplicativo do console)
Dois exemplos de testes de unidade NUnit
O comportamento de correspondência para os
Exception
tipos é exato (ou seja, um filho NÃO é compatível com nenhum de seus tipos pai).fonte
when
, como faria com qualquer versão docatch (Exception ex) {if (...) {/*handle*/} throw;}
. O valor real dewhen
é que o filtro é executado antes da exceção ser capturada , evitando assim a despesa / pilha corrompida de uma nova tentativa. Ele tira proveito de um recurso CLR que antes era acessível apenas ao VB e MSIL.IsAnyOf
método pode ser reescrita como simplesmentep_comparisons.Contains(p_parameter)
Como senti que essas respostas tocaram a superfície, tentei me aprofundar um pouco mais.
Então, o que realmente queremos fazer é algo que não seja compilado, digamos:
A razão pela qual queremos isso é porque não queremos que o manipulador de exceções capture as coisas que precisamos posteriormente no processo. Claro, podemos pegar uma exceção e verificar com um 'se' o que fazer, mas sejamos honestos, não queremos isso de verdade. (FxCop, problemas de depuração, feiura)
Então, por que esse código não é compilado - e como podemos cortá-lo da maneira que ele irá?
Se olharmos para o código, o que realmente queremos fazer é encaminhar a chamada. No entanto, de acordo com a MS Partition II, os blocos manipuladores de exceção IL não funcionarão dessa maneira, o que, neste caso, faz sentido, porque isso implicaria que o objeto 'exceção' pode ter tipos diferentes.
Ou, para escrevê-lo em código, pedimos ao compilador que faça algo assim (bem, não está totalmente correto, mas é a coisa mais próxima possível, eu acho):
A razão pela qual isso não será compilado é bastante óbvia: que tipo e valor o objeto '$ exception' teria (que são armazenados aqui nas variáveis 'e')? A maneira como queremos que o compilador lide com isso é observar que o tipo base comum de ambas as exceções é 'Exception', use isso para que uma variável contenha as duas exceções e, em seguida, lide apenas com as duas exceções detectadas. A maneira como isso é implementado no IL é como 'filtro', disponível no VB.Net.
Para fazê-lo funcionar em C #, precisamos de uma variável temporária com o tipo base 'Exception' correto. Para controlar o fluxo do código, podemos adicionar alguns ramos. Aqui vai:
As desvantagens óbvias para isso são que não podemos voltar a jogar corretamente e, bem, sejamos honestos, que é uma solução feia. A feiura pode ser corrigida um pouco através da eliminação de ramificações, o que torna a solução um pouco melhor:
Isso deixa apenas o 're-lançamento'. Para que isso funcione, precisamos ser capazes de executar o tratamento dentro do bloco 'catch' - e a única maneira de fazer esse trabalho é usando um objeto 'Exception'.
Neste ponto, podemos adicionar uma função separada que lida com os diferentes tipos de exceções usando a resolução de sobrecarga ou para lidar com a exceção. Ambos têm desvantagens. Para começar, aqui está o caminho para fazê-lo com uma função auxiliar:
E a outra solução é capturar o objeto Exception e manipulá-lo adequadamente. A tradução mais literal para isso, com base no contexto acima, é esta:
Então, para concluir:
fonte
Este é um problema clássico que todo desenvolvedor de C # enfrenta eventualmente.
Deixe-me dividir sua pergunta em duas perguntas. O primeiro,
Posso capturar várias exceções ao mesmo tempo?
Em suma, não.
O que leva à próxima pergunta,
Como evito escrever código duplicado, pois não consigo capturar vários tipos de exceção no mesmo bloco catch ()?
Dada sua amostra específica, em que o valor de retorno é barato de construir, eu gosto de seguir estas etapas:
Portanto, o código se parece com:
Se alguma exceção for lançada, o WebId nunca será definido como o valor semi-construído e permanecerá Guid.Empty.
Se a construção do valor de fallback for cara e a redefinição de um valor for muito mais barata, eu moveria o código de redefinição para sua própria função:
fonte
Então você está repetindo muito código em cada opção de exceção? Parece que extrair um método seria uma ideia divina, não é?
Portanto, seu código se resume a isso:
Eu me pergunto por que ninguém percebeu essa duplicação de código.
No C # 6, você também tem os filtros de exceção, como já mencionado por outros. Então você pode modificar o código acima para isso:
fonte
Queria adicionar minha resposta curta a esta discussão já longa. Algo que não foi mencionado é a ordem de precedência das instruções de captura, mais especificamente, você precisa estar ciente do escopo de cada tipo de exceção que está tentando capturar.
Por exemplo, se você usar uma exceção "catch-all" como Exception, ela precederá todas as outras instruções de captura e, obviamente, você obterá erros de compilador; no entanto, se você reverter a ordem, poderá encadear suas instruções de captura (um pouco do anti-padrão, acho). ), você pode colocar o tipo de exceção global na parte inferior e isso capturará todas as exceções que não atenderam a níveis mais altos na sua tentativa ... bloco de captura:
Eu recomendo que as pessoas revejam este documento do MSDN:
Hierarquia de exceção
fonte
Talvez tente manter seu código simples, como colocar o código comum em um método, como faria em qualquer outra parte do código que não esteja dentro de uma cláusula catch?
Por exemplo:
Como eu faria isso, tentando encontrar o padrão simples e bonito
fonte
Observe que eu encontrei uma maneira de fazer isso, mas isso parece mais com o material do The Daily WTF :
fonte
Vale mencionar aqui. Você pode responder às várias combinações (erro de exceção e mensagem de exceção).
Encontrei um cenário de caso de uso ao tentar converter o objeto de controle em um datagrid, com conteúdo como TextBox, TextBlock ou CheckBox. Nesse caso, a exceção retornada era a mesma, mas a mensagem variava.
fonte
Quero sugerir a resposta mais curta (mais um estilo funcional ):
Para isso, é necessário criar várias sobrecargas do método "Catch", semelhantes ao System.Action:
e assim por diante quantas você desejar. Mas você precisa fazer uma vez e pode usá-lo em todos os seus projetos (ou, se você criou um pacote nuget, também podemos usá-lo).
E implementação do CatchMany:
ps Não coloquei verificações nulas para simplificar o código, considere adicionar validações de parâmetro.
ps2 Se você deseja retornar um valor da captura, é necessário fazer os mesmos métodos de captura, mas com retornos e Func em vez de Ação nos parâmetros.
fonte
Basta ligar para tentar e pegar duas vezes.
É simples assim !!
fonte
No c # 6.0, os filtros de exceção são aprimoramentos para o tratamento de exceções
fonte
catch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; }