Catch multiple exceptions at once?

前端 未结 27 1912
夕颜
夕颜 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:11

    With C# 7 the answer from Michael Stum can be improved while keeping the readability of a switch statement:

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

    And with C# 8 as switch expression:

    catch (Exception ex)
    {
        WebId = ex switch
        {
            _ when ex is FormatException || ex is OverflowException => Guid.Empty,
            _ => throw ex
        };
    }
    
    0 讨论(0)
  • 2020-11-22 12:11
    catch (Exception ex)
    {
        if (!(
            ex is FormatException ||
            ex is OverflowException))
        {
            throw;
        }
        Console.WriteLine("Hello");
    }
    
    0 讨论(0)
  • 2020-11-22 12:14

    This is a classic problem every C# developer faces eventually.

    Let me break your question into 2 questions. The first,

    Can I catch multiple exceptions at once?

    In short, no.

    Which leads to the next question,

    How do I avoid writing duplicate code given that I can't catch multiple exception types in the same catch() block?

    Given your specific sample, where the fall-back value is cheap to construct, I like to follow these steps:

    1. Initialize WebId to the fall-back value.
    2. Construct a new Guid in a temporary variable.
    3. Set WebId to the fully constructed temporary variable. Make this the final statement of the try{} block.

    So the code looks like:

    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) {}
    

    If any exception is thrown, then WebId is never set to the half-constructed value, and remains Guid.Empty.

    If constructing the fall-back value is expensive, and resetting a value is much cheaper, then I would move the reset code into its own function:

    try
    {
        WebId = new Guid(queryString["web"]);
        // More initialization code goes here.
    }
    catch (FormatException) {
        Reset(WebId);
    }
    catch (OverflowException) {
        Reset(WebId);
    }
    
    0 讨论(0)
  • 2020-11-22 12:15

    Joseph Daigle's Answer is a good solution, but I found the following structure to be a bit tidier and less error prone.

    catch(Exception ex)
    {   
        if (!(ex is SomeException || ex is OtherException)) throw;
    
        // Handle exception
    }
    

    There are a few advantages of inverting the expression:

    • A return statement is not necessary
    • The code isn't nested
    • There's no risk of forgetting the 'throw' or 'return' statements that in Joseph's solution are separated from the expression.

    It can even be compacted to a single line (though not very pretty)

    catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw;
    
        // Handle exception
    }
    

    Edit: The exception filtering in C# 6.0 will make the syntax a bit cleaner and comes with a number of other benefits over any current solution. (most notably leaving the stack unharmed)

    Here is how the same problem would look using C# 6.0 syntax:

    catch(Exception ex) when (ex is SomeException || ex is OtherException)
    {
        // Handle exception
    }
    
    0 讨论(0)
  • 2020-11-22 12:15

    It is worth mentioning here. You can respond to the multiple combinations (Exception error and exception.message).

    I ran into a use case scenario when trying to cast control object in a datagrid, with either content as TextBox, TextBlock or CheckBox. In this case the returned Exception was the same, but the message varied.

    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
    } 
    
    0 讨论(0)
  • 2020-11-22 12:17

    Not in C# unfortunately, as you'd need an exception filter to do it and C# doesn't expose that feature of MSIL. VB.NET does have this capability though, e.g.

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

    What you could do is use an anonymous function to encapsulate your on-error code, and then call it in those specific catch blocks:

    Action onError = () => WebId = Guid.Empty;
    try
    {
        // something
    }
    catch (FormatException)
    {
        onError();
    }
    catch (OverflowException)
    {
        onError();
    }
    
    0 讨论(0)
提交回复
热议问题