注意:我很高兴地告诉您,异常过滤器现在采用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;
}
}
类似的案例有:SoapException
,XmlException
......
另一种情况是异常被包装为一般异常中的内部异常,并且捕获逻辑应该依赖于内部异常。
假设我们有一个包含这样的异常的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)
{
...
}
答案 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)
我觉得它非常棘手和令人困惑:
如果您的异常条件也会抛出异常怎么办?应该在何处以及如何处理新的异常?
答案 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();
}