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

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

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

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

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


当前回答

所以你在每个异常开关中重复了很多代码?听起来提取一个方法是个好主意,不是吗?

因此,您的代码归结为:

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 */ }

我想知道为什么没有人注意到代码重复。

从C#6中,您还可以使用其他人已经提到的异常过滤器。因此,您可以将上面的代码修改为:

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

其他回答

正如其他人所指出的,您可以在catch块中使用if语句来确定发生了什么。C#6支持异常过滤器,因此以下操作将起作用:

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

MyFilter方法可能看起来像这样:

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

或者,这可以全部内联完成(when语句的右侧必须是布尔表达式)。

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

这与在catch块中使用if语句不同,使用异常过滤器不会展开堆栈。

您可以下载Visual Studio 2015来查看。

如果要继续使用Visual Studio 2013,可以安装以下nuget包:

安装程序包Microsoft.Net.Compilers

在撰写本文时,这将包括对C#6的支持。

引用此包将导致使用中包含的C#和Visual Basic编译器的特定版本与任何系统安装版本不同。

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

or

catch (Exception ex)
{
    if (ex is not FormatException and not OverflowException)
        throw;

    WebId = Guid.Empty;
}

想在这条已经很长的线索上加上我的简短回答。还没有提到的是catch语句的优先顺序,更具体地说,您需要了解您试图捕获的每种类型的异常的范围。

例如,如果您使用“catch-all”异常作为异常,它将优先于所有其他catch语句,您显然会遇到编译器错误。但是,如果您颠倒了catch语句的顺序(我认为这有点反模式),您可以将catch-allException类型放在底部,这将捕获任何在尝试中不适合更高级别的异常。。捕捉块:

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

我强烈建议大家阅读此MSDN文档:

异常层次结构

异常过滤器现在在c#6+中可用。你可以的

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

在C#7.0+中,您也可以将其与模式匹配相结合

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae &&
                           ae.InnerExceptions.Count > tasks.Count/2)
{
   //More than half of the tasks failed maybe..? 
}

也许试着让你的代码保持简单,比如把公共代码放在一个方法中,就像你在代码的任何其他不在catch子句中的部分所做的那样?

例如。:

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

// ...

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

我会怎么做,试图找到简单而美丽的模式