不鼓励只捕获System.Exception。相反,只应捕获“已知”异常。

现在,这有时会导致不必要的重复代码,例如:

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

我想知道:是否有一种方法可以捕获两个异常,并且只调用WebId=Guid.Empty调用一次?

给定的示例相当简单,因为它只是一个GUID。但是想象一下,在代码中,您多次修改一个对象,如果其中一个操作预期失败,您希望“重置”该对象。然而,如果有意外的异常,我仍然想把它推得更高。


当前回答

C#9的更新

使用C#9中的新模式匹配增强功能,可以缩短异常过滤器中的表达式。现在,捕获多个异常很简单,如下所示:

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

其他回答

在C#6中,推荐的方法是使用异常过滤器,下面是一个示例:

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

我想建议最简短的答案(一种更实用的方式):

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

为此,您需要创建几个“Catch”方法重载,类似于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));
    }

你想多少就多少。但是你需要做一次,你可以在所有项目中使用它(或者,如果你创建了一个nuget包,我们也可以使用它)。

以及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.为了代码的简单性,我没有进行空检查,请考虑添加参数验证。

第2页如果要从catch返回值,则必须执行相同的catch方法,但在参数中使用return和Func而不是Action。

这是马特的答案的变体(我觉得这有点干净)。。。使用一种方法:

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

    WebId = Guid.Empty;
}

将引发任何其他异常,代码WebId=Guid.Empty;不会被击中。如果您不希望其他异常使您的程序崩溃,只需在其他两个捕获之后添加这个:

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

使用C#7,Michael Stum的答案可以得到改进,同时保持switch语句的可读性:

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

由于Orace注释,这可以通过省略丢弃变量而用C#8来简化:

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

使用C#8作为开关表达式:

catch (Exception ex)
{
    WebId = ex switch
    {
        _ when ex is FormatException || ex is OverflowException => Guid.Empty,
        _ => throw ex
    };
}

正如内切米娅·霍夫曼指出的那样。后一个示例将导致堆栈丢失。这可以通过使用Jürgen Steinblock描述的扩展方法来防止,以在投掷前捕获堆叠:

catch (Exception ex)
{
    WebId = ex switch
    {
        _ when ex is FormatException || ex is OverflowException => Guid.Empty,
        _ => throw ex.Capture()
    };
}

public static Exception Capture(this Exception ex)
{
    ExceptionDispatchInfo.Capture(ex).Throw();
    return ex;
}

这两种样式都可以通过C#9的模式匹配增强来简化:

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

catch (Exception ex)
{
    WebId = ex switch
    {
        _ when ex is FormatException or OverflowException => Guid.Empty,
        _ => throw ex.Capture()
    };
}

注意,我确实找到了一种方法,但这看起来更像是《每日WTF》的素材:

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