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

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

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

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

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


当前回答

怎么样

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

其他回答

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

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
}

为了完整起见,自.NET 4.0以来,代码可以重写为:

Guid.TryParse(queryString["web"], out WebId);

TryParse从不抛出异常,如果格式错误,则返回false,将WebId设置为Guid.Empty。


由于C#7,您可以避免在单独的行中引入变量:

Guid.TryParse(queryString["web"], out Guid webId);

您还可以创建用于解析返回元组的方法,这些方法在.NET Framework 4.6版之前尚不可用:

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

并像这样使用它们:

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

当在C#12中实现out参数的解构时,这个无用答案的下一个无用更新就来了

在c#6.0中,异常过滤器是对异常处理的改进

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

编辑:我确实同意其他人的观点,即从C#6.0开始,异常过滤器现在是一个非常好的方法:在(ex是…||ex是…)时捕获(exception ex)

除了我仍然有点讨厌一行长的布局,我个人会像下面这样布局代码。我认为这既实用又美观,因为我相信它能提高理解力。有些人可能不同意:

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

因为它当然不会自动变得更可读。

当然,我留下了/*写入日志的三个相同实例,不管怎样…*/回来在第一个例子中。

但这是我的观点。你们都听说过函数/方法,对吧?认真地编写一个通用的ErrorHandler函数,然后从每个catch块调用它。

若你们问我,第二个例子(带有If和is关键字)的可读性明显降低,同时在项目的维护阶段也更容易出错。

对于任何可能对编程相对陌生的人来说,维护阶段将占项目整个生命周期的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 (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;
}