Pergunta

É desencorajado simplesmente pegar System.Exception. Em vez disso, apenas o "conhecido" excepções devem ser apanhado.

Agora, isso às vezes leva a código repetitivo desnecessários, 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 ambos exceções e só chamar a chamada WebId = Guid.Empty uma vez

O exemplo dado é bastante simples, como é apenas um GUID . Mas imagine código onde você modificar um objeto várias vezes, e se uma das manipulações falhar de forma esperada, você quer "redefinir" o object. No entanto, se há uma exceção inesperada, eu ainda quero jogar esse superior.

Foi útil?

Solução

System.Exception Catch and interruptor sobre os tipos

catch (Exception ex)            
{                
    if (ex is FormatException || ex is OverflowException)
    {
        WebId = Guid.Empty;
        return;
    }

    throw;
}

Outras dicas

EDIT: eu concordo com os outros que estão dizendo que, a partir de C # 6.0, filtros de exceção são agora uma maneira perfeitamente bem para ir: catch (Exception ex) when (ex is ... || ex is ... )

Só que eu ainda tipo de ódio o layout de um longa-line e pessoalmente colocar o código para fora como o seguinte. Eu acho que isso é tão funcional como é estético, pois acredito que melhora a compreensão. Alguns podem discordar:

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

ORIGINAL:

Eu sei que sou um pouco tarde para a festa aqui, mas Holy Smoke ...

corte direto ao assunto, este tipo de duplicatas uma resposta mais cedo, mas se você realmente quer para executar uma ação comum para vários tipos de exceção e manter a coisa toda arrumada no âmbito do um método, porque não basta usar um fechamento de função lambda / / inline para fazer algo como o seguinte? Quer dizer, as chances são boas bonito que você vai acabar percebendo que você só quer fazer que o fechamento de um método separado que você pode utilizar em todo o lugar. Mas então ele vai ser super fácil de fazer isso sem alterar o resto do código estruturalmente. Certo?

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

Eu não posso ajudar, mas pergunto ( Aviso: um pouco de ironia / sarcasmo frente) porque na terra a todo esse esforço para, basicamente, apenas substituir o seguinte:

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

... com alguma variação louco desta próxima cheiro de código, eu exemplo média, apenas para fingir que você está salvando algumas teclas.

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

Porque certamente não é automaticamente mais legível.

Com certeza, eu deixei as três instâncias idênticas de /* write to a log, whatever... */ return; fora do primeiro exemplo.

Mas isso é uma espécie de meu ponto. Vocês ouviram falar de funções / métodos, certo? Seriamente. Escreva uma função ErrorHandler comum e, como, chamá-lo de cada bloco catch.

Se você me perguntar, o segundo exemplo (com as palavras-chave if e is) é ao mesmo tempo significativamente menos legível, e ao mesmo tempo significativamente mais durante a fase de manutenção do seu projeto propenso a erros.

A fase de manutenção, para qualquer um que possa estar relativamente novo para programação, vai compreendem 98,7% ou mais do tempo de vida global do seu projecto, e os pobres schmuck fazendo a manutenção é quase certo que vai ser alguém diferente de você . E há uma chance muito boa que vai gastar 50% do seu tempo no trabalho xingando seu nome.

E, claro FxCop late para você e então você tem que também adicionar um atributo ao seu código que tem precisamente zip a ver com o programa em execução, e só é lá para dizer FxCop para ignorar um problema que, em 99,9% dos casos, é totalmente correto em sinalização. E, desculpe, eu poderia estar enganado, mas não que "ignorar" atributo acabam realmente compilado em seu aplicativo?

Será que colocar todo o teste if em uma linha torná-lo mais legível? Acho que não. Quer dizer, eu tinha um outro programador veementemente argumentam uma vez há muito tempo que colocar mais código em uma linha tornaria "correr mais rápido." Mas é claro que ele estava louco delirante Stark. Tentando explicar a ele (com uma cara séria - que estava desafiando) como o intérprete ou compilador iria quebrar essa longa linha à parte, a discretas declarações de uma instrução por linha - essencialmente idêntico ao resultado se tivesse ido em frente e acaba de fazer o código legível em vez de tentar out-inteligente o compilador - não teve nenhum efeito sobre ele qualquer. Mas estou divagando.

Quanto menos legível faz isso começa quando você adicionar mais três tipos de exceção, um mês ou dois a partir de agora? . (Resposta: ele recebe um muito menos legível)

Um dos pontos principais, realmente, é que a maior parte do ponto de formatação do código fonte textual que estamos todos olhando para todos os dias é torná-lo muito, muito óbvio para outros seres humanos que está realmente acontecendo quando o código é executado.Porque o compilador transforma o código-fonte em algo totalmente diferente e não poderia me importar menos sobre o seu código de formatação de estilo. Então, tudo-em-um-line totalmente suga, também.

Apenas dizendo ...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

Como outros apontaram, você pode ter uma declaração if dentro do seu bloco catch para determinar o que está acontecendo. C # 6 suportes de exceção filtros, para o seguinte irá funcionar:

try { … }
catch (Exception e) when (MyFilter(e))
{
    …
}

O método MyFilter poderia então algo parecido com isto:

private bool MyFilter(Exception e)
{
  return e is ArgumentNullException || e is FormatException;
}

Como alternativa, isso pode ser tudo em linha done (do lado direito da declaração, quando só tem que ser uma expressão booleana).

try { … }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
    …
}

Este é diferente de usar uma declaração if de dentro do bloco catch, usando filtros de exceção não desanuviar a pilha.

Você pode baixar o Visual Studio 2015 para verificar esta a.

Se você quiser continuar usando o Visual Studio 2013, você pode instalar o seguinte pacote NuGet:

Instale-Package Microsoft.Net.Compilers

Na época da escrita, isso vai incluir suporte para C # 6.

Fazendo referência a este pacote fará com que o projeto a ser construído usando o versão específica do C # e Visual Basic compiladores contidas no pacote, em oposição a qualquer versão do sistema instalado.

Não em C #, infelizmente, como você precisa de um filtro de exceção para fazê-lo e não C # Não exponha essa característica de MSIL. VB.NET não tem essa capacidade, porém, por exemplo.

Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

O que você poderia fazer é usar uma função anônima para encapsular o código on-erro, e então chamá-lo naqueles blocos catch específicos:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}

Por uma questão de exaustividade, uma vez que .NET 4.0 o código pode reescrita como:

Guid.TryParse(queryString["web"], out WebId);

TryParse não gera exceções e retornos falsos se formato está errado, definindo webid para Guid.Empty.


Desde C # 7 você pode evitar a introdução de uma variável em uma linha separada:

Guid.TryParse(queryString["web"], out Guid webId);

Você também pode criar métodos para analisar tuplas que retornam, que não estão disponíveis no .NET Framework ainda a partir da versão 4.6:

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

E usá-los como este:

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

Próxima atualização inútil para esta resposta inútil vem quando desconstrução de fora-parâmetros é implementado em C # 12.:)

Se você pode atualizar seu aplicativo para C # 6 você tiver sorte. O novo C # versão implementou Filtros de exceção. Então você pode escrever o seguinte:

catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
    WebId = Guid.Empty;
}

Algumas pessoas pensam que este código é o mesmo que

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}

Mas nem IT'S. Na verdade, esta é a única característica nova em C # 6, que não é possível emular em versões anteriores. Em primeiro lugar, um meio de re-jogar mais gerais que pular o prendedor. Em segundo lugar, não é semanticamente equivalente. O novo recurso preserva a pilha intacto quando você está depurando seu código. Sem esse recurso o despejo de memória é menos útil ou mesmo inútil.

Veja uma discussão sobre isso no CodePlex . E um exemplo mostrando a diferença .

Filtros de exceção estão agora disponíveis em c # 6 +. Você pode fazer

try
{
       WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
     WebId = Guid.Empty;
}

Em C # 7.0 ou superior, você pode combinar isso com correspondência de padrão demasiado

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae )
{
   //do something with members of ae, say ae.InnerExceptions
}

Se você não quiser usar uma declaração if dentro do catch escopos, em C# 6.0 você pode usar Exception Filters sintaxe que já foi apoiada pelo CLR em versões pré-visualizações mas existia apenas em VB.NET / MSIL:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
    WebId = Guid.Empty;
}

Este código vai pegar o Exception apenas quando é um InvalidDataException ou ArgumentNullException.

Na verdade, você pode colocar basicamente qualquer condição dentro dessa cláusula when:

static int a = 8;

...

catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
    Console.WriteLine("Catch");
}

Note que ao contrário de uma declaração if dentro do escopo do catch, Exception Filters não pode jogar Exceptions, e quando o fazem, ou quando a condição não é true, a próxima condição catch serão avaliados em vez disso:

static int a = 7;

static int b = 0;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Saída:. Captura Geral

Quando há mais de um true Exception Filter - o primeiro será aceite:

static int a = 8;

static int b = 4;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Saída:. Captura

E como você pode ver na MSIL o código não é traduzido para declarações if, mas para Filters e Exceptions não pode ser jogar a partir de dentro as áreas marcadas com Filter 1 e Filter 2 mas o filtro jogando o Exception falhará vez, também o valor última comparação empurrado para a pilha antes do comando endfilter irá determinar o sucesso / fracasso do filtro (Catch 1 XOR Catch 2 irá executar em conformidade):

 Exceção Filtros MSIL

Além disso, especificamente Guid tem o método Guid.TryParse .

A resposta aceita parece aceitável, exceto que CodeAnalysis / FxCop vai reclamar sobre o fato de que é pegar um tipo de exceção geral.

Além disso, parece que o "é" operador pode degradar o desempenho ligeiramente.

CA1800: Não molde desnecessariamente diz "considerar testar o resultado do 'como' operador de vez", mas se você fizer isso, você estará escrevendo mais código do que se você pegar cada exceção separadamente.

De qualquer forma, aqui está o que eu faria:

bool exThrown = false;

try
{
    // Something
}
catch (FormatException) {
    exThrown = true;
}
catch (OverflowException) {
    exThrown = true;
}

if (exThrown)
{
    // Something else
}

em C # 6 a abordagem recomendada é usar exceção Filtros, aqui está um exemplo:

 try
 {
      throw new OverflowException();
 }
 catch(Exception e ) when ((e is DivideByZeroException) || (e is OverflowException))
 {
       // this will execute iff e is DividedByZeroEx or OverflowEx
       Console.WriteLine("E");
 }

Com C # 7 a resposta de Michael Stum pode ser melhorada, mantendo a legibilidade de uma instrução switch:

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

Esta é uma variante da resposta de Matt (eu sinto que isso é um pouco mais limpa) ... usar um método:

public void TryCatch(...)
{
    try
    {
       // something
       return;
    }
    catch (FormatException) {}
    catch (OverflowException) {}

    WebId = Guid.Empty;
}

Todas as outras exceções vai ser jogado eo WebId = Guid.Empty; código não será atingido. Se você não quer que outras exceções para travar o seu programa, basta adicionar esta APÓS as outras duas capturas:

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}

O Joseph Daigle Resposta é uma boa solução, mas eu achei a seguinte estrutura para ser uma mais arrumado pouco e menos propenso a erros.

catch(Exception ex)
{   
    if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

Existem algumas vantagens de invertendo a expressão:

  • A instrução de retorno não é necessário
  • O código não está aninhado
  • Não há nenhum risco de esquecer a 'atirar' ou 'retorno' declarações que em solução de Joseph são separados a partir da expressão.

Ele pode até mesmo ser compactado para uma única linha (embora não muito bonita)

catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

Editar: O href="http://www.codeproject.com/Articles/853207/What-s-new-in-Csharp-Exception-Filters" rel="nofollow noreferrer"> exceção em C # 6.0 fará com que a sintaxe um pouco mais limpo e vem com uma série de outros benefícios sobre qualquer solução atual. (Mais notavelmente deixando a pilha ileso)

Aqui está como o mesmo problema ficaria usando C # 6.0 sintaxe:

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}

@Micheal

versão ligeiramente revista do seu código:

catch (Exception ex)
{
   Type exType = ex.GetType();
   if (exType == typeof(System.FormatException) || 
       exType == typeof(System.OverflowException)
   {
       WebId = Guid.Empty;
   } else {
      throw;
   }
}

Comparações de strings são feios e lento.

Como cerca

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}

Cautioned e advertiu: No entanto, outro tipo , estilo funcional.

O que está no link não responder à sua pergunta diretamente, mas é trivial para estendê-lo para olhar como:

static void Main() 
{ 
    Action body = () => { ...your code... };

    body.Catch<InvalidOperationException>() 
        .Catch<BadCodeException>() 
        .Catch<AnotherException>(ex => { ...handler... })(); 
}

(Basicamente fornecer outra sobrecarga Catch vazio que se retorna)

A grande questão para isso é por . Eu não acho que o custo supera o ganho aqui:)

catch (Exception ex)
{
    if (!(
        ex is FormatException ||
        ex is OverflowException))
    {
        throw;
    }
    Console.WriteLine("Hello");
}

Atualização 2015/12/15: Veja https://stackoverflow.com/a/22864936/1718702 para C # 6. É um produto de limpeza e agora padrão na língua.

Orientada para as pessoas que querem um mais elegante solução para pegar uma vez e filtro exceções, eu uso um método de extensão como demonstrado abaixo.

Eu já tinha essa extensão em minha biblioteca, originalmente escrito para outros fins, mas funcionou perfeitamente para a verificação type em exceções. Além disso, IMHO, parece mais limpo do que um monte de declarações ||. Além disso, ao contrário da resposta aceita, eu prefiro a manipulação explícita exceção para ex is ... teve comportamento undesireable como classes derrived são atribuíveis a lá tipos de pais).

Uso

if (ex.GetType().IsAnyOf(
    typeof(FormatException),
    typeof(ArgumentException)))
{
    // Handle
}
else
    throw;

IsAnyOf.cs Extensão (See Full Error Handling Exemplo para Dependências)

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }
    }
}

Erro completa Handling Exemplo (Copy-Paste para novo aplicativo Console)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.FluentValidation;

namespace IsAnyOfExceptionHandlerSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // High Level Error Handler (Log and Crash App)
            try
            {
                Foo();
            }
            catch (OutOfMemoryException ex)
            {
                Console.WriteLine("FATAL ERROR! System Crashing. " + ex.Message);
                Console.ReadKey();
            }
        }

        static void Foo()
        {
            // Init
            List<Action<string>> TestActions = new List<Action<string>>()
            {
                (key) => { throw new FormatException(); },
                (key) => { throw new ArgumentException(); },
                (key) => { throw new KeyNotFoundException();},
                (key) => { throw new OutOfMemoryException(); },
            };

            // Run
            foreach (var FooAction in TestActions)
            {
                // Mid-Level Error Handler (Appends Data for Log)
                try
                {
                    // Init
                    var SomeKeyPassedToFoo = "FooParam";

                    // Low-Level Handler (Handle/Log and Keep going)
                    try
                    {
                        FooAction(SomeKeyPassedToFoo);
                    }
                    catch (Exception ex)
                    {
                        if (ex.GetType().IsAnyOf(
                            typeof(FormatException),
                            typeof(ArgumentException)))
                        {
                            // Handle
                            Console.WriteLine("ex was {0}", ex.GetType().Name);
                            Console.ReadKey();
                        }
                        else
                        {
                            // Add some Debug info
                            ex.Data.Add("SomeKeyPassedToFoo", SomeKeyPassedToFoo.ToString());
                            throw;
                        }
                    }
                }
                catch (KeyNotFoundException ex)
                {
                    // Handle differently
                    Console.WriteLine(ex.Message);

                    int Count = 0;
                    if (!Validate.IsAnyNull(ex, ex.Data, ex.Data.Keys))
                        foreach (var Key in ex.Data.Keys)
                            Console.WriteLine(
                                "[{0}][\"{1}\" = {2}]",
                                Count, Key, ex.Data[Key]);

                    Console.ReadKey();
                }
            }
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }

        /// <summary>
        /// Validates if any passed in parameter is equal to null.
        /// </summary>
        /// <param name="p_parameters">Parameters to test for Null.</param>
        /// <returns>True if one or more parameters are null.</returns>
        public static bool IsAnyNull(params object[] p_parameters)
        {
            p_parameters
                .CannotBeNullOrEmpty("p_parameters");

            foreach (var item in p_parameters)
                if (item == null)
                    return true;

            return false;
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void CannotBeNull(this object p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw
                    new
                        ArgumentNullException(
                        string.Format("Parameter \"{0}\" cannot be null.",
                        p_name), default(Exception));
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void CannotBeNullOrEmpty<T>(this ICollection<T> p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw new ArgumentNullException("Collection cannot be null.\r\nParameter_Name: " + p_name, default(Exception));

            if (p_parameter.Count <= 0)
                throw new ArgumentOutOfRangeException("Collection cannot be empty.\r\nParameter_Name: " + p_name, default(Exception));
        }

        /// <summary>
        /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentException"></exception>
        public static void CannotBeNullOrEmpty(this string p_parameter, string p_name)
        {
            if (string.IsNullOrEmpty(p_parameter))
                throw new ArgumentException("String cannot be null or empty.\r\nParameter_Name: " + p_name, default(Exception));
        }
    }
}

duas amostras NUnit testes de unidade

comportamento Matching para tipos Exception é exata (ie. A criança não é um jogo para qualquer um dos seus tipos de pais).

using System;
using System.Collections.Generic;
using Common.FluentValidation;
using NUnit.Framework;

namespace UnitTests.Common.Fluent_Validations
{
    [TestFixture]
    public class IsAnyOf_Tests
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void IsAnyOf_ArgumentNullException_ShouldNotMatch_ArgumentException_Test()
        {
            Action TestMethod = () => { throw new ArgumentNullException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(ArgumentException), /*Note: ArgumentNullException derrived from ArgumentException*/
                    typeof(FormatException),
                    typeof(KeyNotFoundException)))
                {
                    // Handle expected Exceptions
                    return;
                }

                //else throw original
                throw;
            }
        }

        [Test, ExpectedException(typeof(OutOfMemoryException))]
        public void IsAnyOf_OutOfMemoryException_ShouldMatch_OutOfMemoryException_Test()
        {
            Action TestMethod = () => { throw new OutOfMemoryException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(OutOfMemoryException),
                    typeof(StackOverflowException)))
                    throw;

                /*else... Handle other exception types, typically by logging to file*/
            }
        }
    }
}

Uma vez que eu me senti como essas respostas apenas tocou a superfície, eu tentou cavar um pouco mais.

Então, o que nós realmente queremos fazer é algo que não compilar, dizer:

// Won't compile... damn
public static void Main()
{
    try
    {
        throw new ArgumentOutOfRangeException();
    }
    catch (ArgumentOutOfRangeException)
    catch (IndexOutOfRangeException) 
    {
        // ... handle
    }

A razão nós queremos isso é porque não queremos que o manipulador de exceção para as coisas de captura que precisamos de mais tarde no processo. Claro, podemos pegar uma exceção e verifique com um 'se' o que fazer, mas vamos ser honestos, nós realmente não quero isso. (FxCop, questões depurador, uglyness)

Então, por que não vai este código de compilação? - e como podemos cortá-lo de tal forma que ele irá

Se olharmos para o código, o que realmente gostaria de fazer é desviar a chamada. No entanto, de acordo com a partição MS II, IL blocos manipulador de exceção não trabalho como este, que, neste caso, faz sentido, porque isso implicaria que o objeto 'exceção' podem ter tipos diferentes.

Ou para escrevê-lo em código, pedimos ao compilador para fazer algo assim (bem, não é inteiramente correto, mas é o mais próximo possível coisa eu acho):

// Won't compile... damn
try
{
    throw new ArgumentOutOfRangeException();
}
catch (ArgumentOutOfRangeException e) {
    goto theOtherHandler;
}
catch (IndexOutOfRangeException e) {
theOtherHandler:
    Console.WriteLine("Handle!");
}

A razão que isso não irá compilação é bastante óbvio: que tipo e valor que o objeto '$ exceção' tem (que são aqui armazenados nas variáveis ??'e')? A forma como queremos que o compilador para lidar com isso é notar que o tipo base comum de ambas as exceções é 'exceção', o uso que para uma variável para conter ambas as exceções, e depois lidar com apenas as duas exceções que são capturados. A forma como isso é implementado em IL é como 'filtro', que está disponível em VB.Net.

Para fazê-lo funcionar em C #, precisamos de uma variável temporária com a 'exceção' tipo base correta. Para controlar o fluxo do código, podemos adicionar alguns ramos. Aqui vai:

    Exception ex;
    try
    {
        throw new ArgumentException(); // for demo purposes; won't be caught.
        goto noCatch;
    }
    catch (ArgumentOutOfRangeException e) {
        ex = e;
    }
    catch (IndexOutOfRangeException e) {
        ex = e;
    }

    Console.WriteLine("Handle the exception 'ex' here :-)");
    // throw ex ?

noCatch:
    Console.WriteLine("We're done with the exception handling.");

As desvantagens óbvias para isso são que não podemos re-jogar corretamente e Bem vamos ser honesto, que é bastante a solução feio. O uglyness pode ser fixado um pouco por eliminação realizando ramo, o que torna a solução um pouco melhor:

Exception ex = null;
try
{
    throw new ArgumentException();
}
catch (ArgumentOutOfRangeException e)
{
    ex = e;
}
catch (IndexOutOfRangeException e)
{
    ex = e;
}
if (ex != null)
{
    Console.WriteLine("Handle the exception here :-)");
}

Isso deixa apenas o 're-lance'. Para que isso funcione, é preciso ser capaz de realizar o tratamento dentro do bloco 'pegar' - ea única maneira de fazer este trabalho é por um objeto 'Exceção' captura.

Neste ponto, podemos adicionar uma função separada que lida com os diferentes tipos de exceções usando resolução de sobrecarga, ou para tratar a exceção. Ambos têm desvantagens. Para começar, aqui é a maneira de fazê-lo com uma função auxiliar:

private static bool Handle(Exception e)
{
    Console.WriteLine("Handle the exception here :-)");
    return true; // false will re-throw;
}

public static void Main()
{
    try
    {
        throw new OutOfMemoryException();
    }
    catch (ArgumentException e)
    {
        if (!Handle(e)) { throw; }
    }
    catch (IndexOutOfRangeException e)
    {
        if (!Handle(e)) { throw; }
    }

    Console.WriteLine("We're done with the exception handling.");

e a outra solução é a de capturar o objecto de excepção e manipulá-lo adequadamente. A tradução literal mais para isso, com base no contexto acima é esta:

try
{
    throw new ArgumentException();
}
catch (Exception e)
{
    Exception ex = (Exception)(e as ArgumentException) ?? (e as IndexOutOfRangeException);
    if (ex != null)
    {
        Console.WriteLine("Handle the exception here :-)");
        // throw ?
    }
    else 
    {
        throw;
    }
}

Portanto, para concluir:

  • Se não quiser re-lance, podemos considerar pegar as exceções certas, e armazená-los em um temporário.
  • Se o manipulador é simples, e queremos reutilização de código, a melhor solução é provavelmente a introduzir uma função auxiliar.
  • Se queremos re-lançamento, não temos escolha, mas para colocar o código em um manipulador catch 'exceção', que vai quebrar FxCop e exceções.Ele do seu depurador.

Este é um problema clássico cada desenvolvedor C # enfrenta eventualmente.

Deixe-me quebrar sua pergunta para 2 perguntas. A primeira,

Can I captura várias exceções de uma só vez?

Em suma, não.

o que leva à próxima pergunta,

Como posso evitar escrever código duplicado dado que eu não posso pegar vários tipos de exceção no mesmo catch () bloco?

Dada a sua amostra específica, onde o valor de recuo é barato para construir, eu gosto de seguir estes passos:

  1. Inicializar webid ao valor queda-back.
  2. Construir um novo Guid em uma variável temporária.
  3. Set webid à variável temporária totalmente construído. Faça desta a declaração final do bloco try {}.

Assim, os olhares código como:

try
{
    WebId = Guid.Empty;
    Guid newGuid = new Guid(queryString["web"]);
    // More initialization code goes here like 
    // newGuid.x = y;
    WebId = newGuid;
}
catch (FormatException) {}
catch (OverflowException) {}

Se qualquer exceção é lançada, então webid nunca é definido com o valor construído-metade, e permanece Guid.Empty.

Se o cálculo do valor de recuo é caro, e redefinir um valor é muito mais barato, então eu iria mover o código de reset em sua própria função:

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}

Assim você're repetindo um monte de código dentro de cada exceção-switch? Soa como extrair um método seria idéia deus, Indiferente-lo?

Assim, seu código se resume a isto:

MyClass instance;
try { instance = ... }
catch(Exception1 e) { Reset(instance); }
catch(Exception2 e) { Reset(instance); }
catch(Exception) { throw; }

void Reset(MyClass instance) { /* reset the state of the instance */ }

Eu me pergunto por que ninguém notou que o código-duplicação.

De C # 6, você ainda as de exceção-filtros como já mencionado por outros. Então, você pode modificar o código acima para o seguinte:

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}

Queria acrescentou minha resposta curta para isso já longo fio. Algo que não foi mencionado é a ordem de precedência das declarações de capturas, mais especificamente você precisa estar ciente do âmbito de cada tipo de exceção que você está tentando recuperar.

Por exemplo, se você usar um "pega-tudo" exceção como Exceção que irá preceder todas as outras declarações de captura e você vai, obviamente, obter erros do compilador se você inverter a ordem você pode encadear-se a sua captura declarações (pouco de um anti-padrão eu acho) você pode colocar o catch-all Exceção tipo na parte inferior e este será captura todas as exceções que não servem para mais acima na sua tentativa .. catch bloco:

            try
            {
                // do some work here
            }
            catch (WebException ex)
            {
                // catch a web excpetion
            }
            catch (ArgumentException ex)
            {
                // do some stuff
            }
            catch (Exception ex)
            {
                // you should really surface your errors but this is for example only
                throw new Exception("An error occurred: " + ex.Message);
            }

Eu recomendo folks avaliar este documento MSDN:

Exceção Hierarquia

Talvez tente manter seu código simples, como colocar o código comum em um método, como você faria em qualquer outra parte do código que não está dentro de uma cláusula catch?

por exemplo:.

try
{
    // ...
}
catch (FormatException)
{
    DoSomething();
}
catch (OverflowException)
{
    DoSomething();
}

// ...

private void DoSomething()
{
    // ...
}

Apenas como eu iria fazê-lo, tentando encontrar o simples é bonito padrão

Note que eu fiz encontrar uma maneira de fazê-lo, mas isso parece mais como material para The Daily WTF :

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

Vale a pena mencionar aqui. Você pode responder às múltiplas combinações (erro de exceção e Exception.Message).

Corri para um cenário de caso de uso ao tentar objeto de controle de elenco em um datagrid, com tanto conteúdo como TextBox, TextBlock ou CheckBox. Neste caso, a exceção retornado era o mesmo, mas a mensagem variada.

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 

Quero sugerir resposta mais curta (um estilo funcional mais ):

        Catch<FormatException, OverflowException>(() =>
            {
                WebId = new Guid(queryString["web"]);
            },
            exception =>
            {
                WebId = Guid.Empty;
            });

Para isso, você precisa criar vários "pegar" sobrecargas do método, semelhante a System.Action:

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2));
    }

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2, TException3>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2), typeof(TException3));
    }

e assim por diante como muitos como você deseja. Mas você precisa fazê-lo uma vez e você pode usá-lo em todos os seus projetos (ou, se você criou um pacote NuGet poderíamos usá-lo também).

E implementação CatchMany:

    [DebuggerNonUserCode]
    public static void CatchMany(Action tryBlock, Action<Exception> catchBlock,
        params Type[] exceptionTypes)
    {
        try
        {
            tryBlock();
        }
        catch (Exception exception)
        {
            if (exceptionTypes.Contains(exception.GetType())) catchBlock(exception);
            else throw;
        }
    }

P.S. Eu não colocar verificações nulos para código de simplicidade, considere adicionar validações dos parâmetros.

p.s.2 Se você quiser retornar um valor da captura, é necessário fazer mesmos métodos de captura, mas com retornos e Func em vez de Ação em parâmetros.

Basta ligar para o try e catch duas vezes.

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
try
{
    WebId = new Guid(queryString["web"]);
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

É simples assim !!

Em c # 6.0, exceção Filtros é melhorias para manipulação de exceção

try
{
    DoSomeHttpRequest();
}
catch (System.Web.HttpException e)
{
    switch (e.GetHttpCode())
    {
        case 400:
            WriteLine("Bad Request");
        case 500:
            WriteLine("Internal Server Error");
        default:
            WriteLine("Generic Error");
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top