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

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

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

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

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


当前回答

编辑:我确实同意其他人的观点,即从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;
}

这里值得一提。您可以响应多个组合(Exceptionerror和Exception.message)。

当我试图在数据网格中转换控件对象时,我遇到了一个用例场景,其中的内容为TextBox、TextBlock或CheckBox。在这种情况下,返回的异常相同,但消息不同。

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 

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

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

C#9的更新

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

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

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

        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。