Catch multiple exceptions at once?

2018-12-31 14:52发布

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

Now, this sometimes leads to unneccessary repetitive code, for example:

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

I wonder: Is there a way to catch both exceptions and only call the WebId = Guid.Empty call once?

The given example is rather simple, as it's only a GUID. But imagine code where you modify an object multiple times, and if one of the manipulations fail in an expected way, you want to "reset" the object. However, if there is an unexpected exception, I still want to throw that higher.

27条回答
余欢
2楼-- · 2018-12-31 15:24

In c# 6.0,Exception Filters is improvements for exception handling

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");
    }
}
查看更多
与风俱净
3楼-- · 2018-12-31 15:25

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. :)

查看更多
伤终究还是伤i
4楼-- · 2018-12-31 15:26

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.

查看更多
登录 后发表回答