Catch multiple exceptions at once?

前端 未结 27 1883
夕颜
夕颜 2020-11-22 11:31

It is discouraged to simply catch System.Exception. Instead, only the "known" exceptions should be caught.

Now, this sometimes leads to unnecce

相关标签:
27条回答
  • 2020-11-22 12:05

    For the sake of completeness, since .NET 4.0 the code can rewritten as:

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

    TryParse never throws exceptions and returns false if format is wrong, setting WebId to Guid.Empty.


    Since C# 7 you can avoid introducing a variable on a separate line:

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

    You can also create methods for parsing returning tuples, which aren't available in .NET Framework yet as of version 4.6:

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

    And use them like this:

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

    Next useless update to this useless answer comes when deconstruction of out-parameters is implemented in C# 12. :)

    0 讨论(0)
  • 2020-11-22 12:05

    If you don't want to use an if statement within the catch scopes, in C# 6.0 you can use Exception Filters syntax which was already supported by the CLR in previews versions but existed only in VB.NET/MSIL:

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

    This code will catch the Exception only when it's a InvalidDataException or ArgumentNullException.

    Actually, you can put basically any condition inside that when clause:

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

    Note that as opposed to an if statement inside the catch's scope, Exception Filters cannot throw Exceptions, and when they do, or when the condition is not true, the next catch condition will be evaluated instead:

    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");
    }
    

    Output: General catch.

    When there is more then one true Exception Filter - the first one will be accepted:

    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");
    }
    

    Output: Catch.

    And as you can see in the MSIL the code is not translated to if statements, but to Filters, and Exceptions cannot be throw from within the areas marked with Filter 1 and Filter 2 but the filter throwing the Exception will fail instead, also the last comparison value pushed to the stack before the endfilter command will determine the success/failure of the filter (Catch 1 XOR Catch 2 will execute accordingly):

    Also, specifically Guid has the Guid.TryParse method.

    0 讨论(0)
  • 2020-11-22 12:05

    The accepted answer seems acceptable, except that CodeAnalysis/FxCop will complain about the fact that it's catching a general exception type.

    Also, it seems the "is" operator might degrade performance slightly.

    CA1800: Do not cast unnecessarily says to "consider testing the result of the 'as' operator instead", but if you do that, you'll be writing more code than if you catch each exception separately.

    Anyhow, here's what I would do:

    bool exThrown = false;
    
    try
    {
        // Something
    }
    catch (FormatException) {
        exThrown = true;
    }
    catch (OverflowException) {
        exThrown = true;
    }
    
    if (exThrown)
    {
        // Something else
    }
    
    0 讨论(0)
  • 2020-11-22 12:06

    Catch System.Exception and switch on the types

    catch (Exception ex)            
    {                
        if (ex is FormatException || ex is OverflowException)
        {
            WebId = Guid.Empty;
            return;
        }
    
        throw;
    }
    
    0 讨论(0)
  • 2020-11-22 12:07

    If you can upgrade your application to C# 6 you are lucky. The new C# version has implemented Exception filters. So you can write this:

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

    Some people think this code is the same as

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

    But it´s not. Actually this is the only new feature in C# 6 that is not possible to emulate in prior versions. First, a re-throw means more overhead than skipping the catch. Second, it is not semantically equivalent. The new feature preserves the stack intact when you are debugging your code. Without this feature the crash dump is less useful or even useless.

    See a discussion about this on CodePlex. And an example showing the difference.

    0 讨论(0)
  • 2020-11-22 12:07

    This is a variant of Matt's answer (I feel that this is a bit cleaner)...use a method:

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

    Any other exceptions will be thrown and the code WebId = Guid.Empty; won't be hit. If you don't want other exceptions to crash your program, just add this AFTER the other two catches:

    ...
    catch (Exception)
    {
         // something, if anything
         return; // only need this if you follow the example I gave and put it all in a method
    }
    
    0 讨论(0)
提交回复
热议问题