用条件捕获异常

时间:2010-09-29 08:19:54

标签: c# exception syntax

注意:我很高兴地告诉您,异常过滤器现在采用C#6.0语言。

这是一个思想实验,我对你的意见感兴趣:这对你有意义吗?你知道C#编程语言是否已经提出了类似的东西吗?我甚至不知道在哪里发送这样的提议......

这个想法是引入语法元素,只有在满足特定条件时才能捕获异常。

一个用例示例是在使用COM Interop时:Everything总是抛出COMException。实际区分错误代码包含在其消息中。

那么(提案1):

try
{
    ...
}
catch (COMException ex where ex.Message.Contains("0x800706BA"))
{
    // RPC server unavailable
}
catch (COMException ex where ex.Message.Contains("0x80010001"))
{
    // Call rejected by callee
}

转换为:

try
{
    ...
}
catch (COMException ex)
{
    if (ex.Message.Contains("0x800706BA"))
    {
        // RPC server unavailable
    }
    else if (ex.Message.Contains("0x80010001"))
    {
        // Call rejected by callee
    }
    else
    {
        throw;
    }
}

类似的案例有:SoapExceptionXmlException ......


另一种情况是异常被包装为一般异常中的内部异常,并且捕获逻辑应该依赖于内部异常。

假设我们有一个包含这样的异常的API:catch (NumberFormatException ex) { throw new BusinessException(ex) }

如何(提案2A):

try
{
    ...
}
catch (inner NumberFormatException nfex)
{
    ...
}

转换为:

catch (Exception ex where ex.InnerException is NumberFormatException)
{
    NumberFormatException nfex = ex.InnerException;
    ...
}

或(2B):

catch (BusinessException bex inner NumberFormatException nfex)
{
    ...
}

转换为:

catch (BusinessException bex where bex.InnerException is NumberFormatException)
{
    NumberFormatException nfex = bex.InnerException;
    ...
}

this case (originally from Java)中,它可能看起来像(2C):

catch (RemoteAccessException raex inner inner MyException mex)
{
    ...
}

7 个答案:

答案 0 :(得分:20)

根据try-catch C# Reference for Visual Studio 2015 RC,现在已实施:

Catch (ArgumentException e) when (e.ParamName == "…")
{
}

答案 1 :(得分:8)

VB.Net具有此异常过滤功能,如下所示

Catch ex As COMException When ex.ErrorCode = 0x800706BA

因此CLR支持此功能,但该功能未在C#

中公开

据说F#也有此功能,但我不太了解F#以显示示例。

答案 2 :(得分:4)

例外与类型密切相关。如果要区分两种不同的异常,则应该生成两种异常类型。在您的示例中,您将获得Com800706BAException和Com80010001Exception。

现在,这并非总是可行或可行,例如,如果底层系统使用错误代码而不是异常。在这种情况下,您的方法可能会有所帮助。但是,这种语言功能很容易被滥用。例如,您可以像这样进行错误处理,这不是类型安全的:

catch (Exception e where e.Message = "The foo barfed up the bar")

如果要检查异常的内部异常,则表明您正在错误的级别上进行错误处理。这个想法是一个方法抛出一个通用异常,从方法的内部工作中抽象出调用者。如果依赖于某些内部异常,则会紧密耦合到该方法的实现。这很糟糕。

应该抛出一个单独的通用异常,或者应该在方法内部移动错误处理。

答案 3 :(得分:2)

为什么要用一种无关紧要的语言来烘焙某些东西?

建议1 可以通过catch中的目标switch语句轻松解决 - 这样您就可以处理所需的COM异常,并重新抛出您不想处理的任何内容。 / p>

提案2 的问题是异常堆栈可能是任意深度的,并且可能(或将会)包含:

  • 同一类型异常的多个嵌套实例 - 您的查询语法应该处理哪一个?

  • 从相同的基本异常*派生的不同异常 - 如果您的查询语法指定了一个较低级别的基本异常,那么它可以匹配堆栈中的一大堆更高级别的异常,哪一个是你想加工吗?

大多数情况下,当您迭代异常堆栈时,您不会对检索堆栈中间的异常感兴趣,而是将遍历异常堆栈并获取第一个用于记录的异常堆栈。其余的时间你只关心最后(外部)异常。我个人不记得有时需要以编程方式捕获/处理埋在堆栈中间某处的异常,它始终是第一个,最后一个或全部。< / p>

*忽略所有异常都来自 System.Exception 这一事实 - 我的意思更多的是 MyBaseException ,所有自定义异常都源于此。

答案 4 :(得分:2)

我觉得它非常棘手和令人困惑:

如果您的异常条件也会抛出异常怎么办?应该在何处以及如何处理新的异常?

  • 围绕一个捕获块尝试/捕捉? MEH ..
  • 让其他catch块处理该异常:StackOverflows yay:)

答案 5 :(得分:1)

我不确定我是那么喜欢它。首先,它听起来像一个非常简洁的想法,但后来我想,如果为这类事情添加语法糖,人们可能会在状态代码更合适时滥用异常。

正如一些人已经指出的那样,这已经在VB中,但你可以在C#中轻松做类似的事情:

catch (Exception ex)
{
  if (ex.Message.Contains("Yikes!"))
  {
    // Do your thing
  }
  ...
  else
  {
    throw;
  }
}

所以,它真的只是语法糖。

关于异常的事情是(正如在本网站上经常讨论的那样)它们违反了程序的顺序结构,可能会跳过很多代码并在你真的不想要的时候弹出堆栈。这就是为什么我不认为它们对任何事情都有好处,只有非常特殊的条件,如果你真的可以以某种方式处理这些条件,那应该有点痛苦(试试{}抓住{if( ..)}等等,以便人们不会使用异常超过这些特殊条件。

答案 6 :(得分:1)

从c#7.0开始,这种东西已经得到了支持。 Here是完整的参考。 这是代码段。

    try
    {
        SomeOperationThatThrowsException();
    }
    catch(TheFilteredException ex ) when (ex.ErrorCode==123)
    {
        DoSomethingWhenExceptionThrown();
    }