协慌网

登录 贡献 社区

一次捕获多个异常?

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

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

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

我想知道:有没有办法捕获两个异常,只调用一次WebId = Guid.Empty

给出的示例相当简单,因为它只是一个GUID 。但是想象一下你多次修改一个对象的代码,如果其中一个操作以预期的方式失败,你想要 “重置” 该object 。但是,如果出现意外异常,我仍然希望将其提高。

答案

Catch System.Exception并打开类型

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

    throw;
}

编辑:我同意其他人的说法,从 C#6.0 开始,异常过滤器现在是一个非常好的方法: catch (Exception ex) when (ex is ... || ex is ... )

除了我仍然讨厌一个长线布局,并将个人像下面那样放置代码。我认为这是功能性的,因为它是审美的,因为我相信它可以提高理解力。有些人可能不同意:

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

原版的:

我知道我在这里参加聚会有点晚了,但圣烟......

直接追逐,这种复制更早的答案,但如果你真的想要为几种异常类型执行一个共同的操作,并保持整个事情在一个方法的范围内整洁,为什么不只是使用 lambda / closure / inline 函数执行以下操作?我的意思是,很有可能你最终会意识到你只想让那个闭包成为一个可以在各地利用的独立方法。但是,如果不在结构上实际更改代码的其余部分,那么这将非常容易。对?

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

我不禁怀疑( 警告:前面有点讽刺 / 讽刺)为什么在地球上去做所有这些努力基本上只是替换以下内容:

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

... 这个下一个代码气味的一些疯狂的变化,我的意思是,只是假装你节省了一些按键。

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

因为它肯定不会自动更具可读性。

当然,我把三个相同的/* write to a log, whatever... */ return;实例留给了/* write to a log, whatever... */ return;在第一个例子中。

但这是我的观点。你们都听说过功能 / 方法,对吧?认真。编写一个常见的ErrorHandler函数,就像从每个 catch 块中调用它一样。

如果您问我,第二个示例(使用ifis关键字)的可读性明显较低,同时在项目的维护阶段更容易出错。

维护阶段,对于任何可能相对较新的编程人员来说,将占项目整体生命周期的 98.7%或更多,而做维护的穷人也几乎肯定会成为除你以外的其他人。并且他们很有可能将 50%的时间花在诅咒你名字的工作上。

当然 FxCop 的呵斥你,所以你必须添加到您的代码属性已精确拉链与正在运行的程序做的,只是没有告诉的 FxCop 忽视的一个问题是,在案件 99.9%,这是完全标记正确。而且,对不起,我可能会弄错,但是那个 “忽略” 属性最终实际编译到你的应用程序中了吗?

将整个if测试放在一行会使它更具可读性吗?我不这么认为。我的意思是,我确实让另一位程序员在很久以前激烈地争辩说,将更多代码放在一行上会使其 “运行得更快”。但当然,他是疯狂的坚果。试图向他解释(直言不讳 - 这很有挑战性)解释器或编译器如何将这条长线分开为离散的单指令每行语句 - 如果他已经取得了进展,那么基本上与结果相同只是使代码可读而不是试图超越编译器 - 对他没有任何影响。但我离题了。

如何可读不会当你从现在添加三个异常类型,一两个月这得到什么? (答案:它的可读性低很多 )。

实际上,其中一个重点是,我们每天都在查看文本源代码的大部分格式是让其他人真正,非常明显地在代码运行时实际发生了什么。因为编译器将源代码转换为完全不同的东西,并且对代码格式化风格无关紧要。所以一对一的线路也很糟糕。

只是说......

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

正如其他人指出的那样,你可以在 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 编译器构建项目,而不是任何系统安装版本。