简体   繁体   English

用条件捕获异常

[英]Catch Exception with Condition

Note: I am very happy to tell you that exception filters are now in the C# 6.0 language. 注意:我很高兴地告诉您,异常过滤器现在使用的是C#6.0语言。

This is a thought experiment, I'm interested in your opinion: Does it make sense to you? 这是一个思想实验,我对你的观点感兴趣:这对你有意义吗? Do you know whether something similar has already been proposed for the C# programming language? 你知道C#编程语言是否已经提出了类似的东西吗? I wouldn't even know where to send such a proposal... 我甚至不知道在哪里提出这样的建议......

The idea is introducing syntax elements to catch an Exception only if it fulfills a certain condition. 这个想法是引入语法元素,只有在满足特定条件时才能捕获异常。

One use case example is when working with COM Interop: Everything always throws a COMException . 一个用例示例是在使用COM Interop时:Everything总是抛出一个COMException The actual distinguishing error code is contained in its message. 实际区分错误代码包含在其消息中。

So what about (proposal 1): 那么(提案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
}

which translates to: 这意味着:

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

Similar cases are: SoapException , XmlException ... 类似的情况是: SoapExceptionXmlException ...


Another scenario is when exceptions are wrapped as inner exceptions within a general exception, and the catching logic should depend on the inner exception. 另一种情况是异常被包装为一般异常中的内部异常,并且捕获逻辑应该依赖于内部异常。

Say we have an API that wraps exceptions like this: catch (NumberFormatException ex) { throw new BusinessException(ex) } . 假设我们有一个包含这样的异常的API: catch (NumberFormatException ex) { throw new BusinessException(ex) }

What about (proposal 2A): 怎么样(提案2A):

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

which translates to: 这意味着:

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

or (2B): 或(2B):

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

which translates to: 这意味着:

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

In this case (originally from Java) it could look like (2C): 这种情况下(最初来自Java)它看起来像(2C):

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

According to the try-catch C# Reference for Visual Studio 2015 RC this is now implemented: 根据Visual Studio 2015 RCtry-catch C#参考,现在实现了:

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

VB.Net has this feature of exception filter as shown below VB.Net具有异常过滤器的这一功能,如下所示

Catch ex As COMException When ex.ErrorCode = 0x800706BA

So this is supported by the CLR but the feature is not exposed in C# 所以这得到了CLR的支持,但是这个功能没有在C#中公开

Supposedly F# has this feature as well but I don't know much about F# to show example. 据说F#也有这个功能,但我不太了解F#来展示例子。

Exceptions and types are tightly related. 例外和类型紧密相关。 If you want to distinguish between two separate kinds of exceptions, you should make two exception types. 如果要区分两种不同的异常,则应该生成两种异常类型。 In your example, you would have a Com800706BAException and a Com80010001Exception. 在您的示例中,您将获得Com800706BAException和Com80010001Exception。

Now, this is not always possible or feasible, for example if the underlying system uses error codes instead of exceptions. 现在,这并不总是可行或可行的,例如,如果底层系统使用错误代码而不是异常。 In that case, your method may be helpful. 在这种情况下,您的方法可能会有所帮助。 However, this language feature would easily be misused. 但是,这种语言功能很容易被滥用。 For example, you could do error handling like this, which is not type-safe: 例如,您可以像这样进行错误处理,这不是类型安全的:

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

If you want to check the inner exception of an exception, you are doing the error handling on the wrong level. 如果要检查异常的内部异常,则表明您正在错误的级别上进行错误处理。 The idea is that a method throws a generalized exception to abstract the caller from the inner working of the method. 这个想法是一个方法抛出一个通用异常,从方法的内部工作中抽象出调用者。 If you depend on some inner exception, you are tightly coupled to the implementation of the method. 如果依赖于某些内部异常,则会紧密耦合到该方法的实现。 This is bad. 这是不好的。

Either a separate, generalized exception should be thrown, or the error handling should be moved inside the method. 应抛出单独的通用异常,或者应在方法内移动错误处理。

Why bake something into the language that is trivial to do anyway? 为什么要用一种无关紧要的语言来烘焙某些东西呢?

Proposal 1 is easily addressed with a targetted switch statement inside the catch - that way you can deal with the COM exceptions that you want and just rethrow anything you don't want to deal with. 使用catch中的目标switch语句可以轻松解决提议1 - 这样您就可以处理所需的COM异常,并重新抛出您不想处理的任何内容。

The problem with Proposal 2 is that the exception stack could be arbitrarily deep, and could (or will) contain: Proposal 2的问题是异常堆栈可能是任意深度的,并且可能(或将)包含:

  • multiple nested instances of the same type of exception - which one should your query syntax deal with? 同一类型的异常的多个嵌套实例 - 您的查询语法应该处理哪一个?

  • different exceptions which derive from the same base exception* - if your query syntax specifies one of the lower level base exceptions then that could match a whole bunch of higher level exceptions in the stack, which one(s) are you looking to process? 从相同的基本异常*派生的不同异常 - 如果您的查询语法指定了一个较低级别的基本异常,那么它可以匹配堆栈中的一大堆更高级别的异常,您希望处理哪一个?

Most of the time when you are iterating the exception stack you won't be interested in retrieving the exception that is halfway down the stack, instead you will walk the exception stack and get the very first one for logging purposes. 在大多数情况下,当您迭代异常堆栈时,您将无法检索堆栈中间的异常,而是将遍历异常堆栈并获取第一个用于记录的异常堆栈。 The rest of the time you only care about the last (outer) exception. 其余的时间你只关心最后(外部)异常。 I personally cannot recall a time where i had a need to programmatically catch/handle an exception that was buried somewhere in the middle of the stack, it has always been first, last, or all of them. 我个人不记得我需要以编程方式捕获/处理埋在堆栈中间某处的异常的时间,它始终是第一个,最后一个或全部。

*disregarding the fact that all exceptions derive from System.Exception - i was meaning more along the lines of MyBaseException which all your custom exceptions derive from. *忽略所有异常都来自System.Exception的事实 - 我更多地意味着所有自定义异常派生自的MyBaseException

I think its pretty tricky and confusing: 我觉得它非常棘手和令人困惑:

What if your exception condition throws an exception as well? 如果您的异常条件也引发异常怎么办? Where and how should that new exception be handled? 应该在何处以及如何处理新的例外?

  • A try/catch around a catch block? 围绕一个捕获区的尝试/捕获? meh.. 咩..
  • Let other catch blocks handled that exception: StackOverflows yay :) 让其他catch块处理该异常:StackOverflows yay :)

I'm not sure I like it that much. 我不确定我喜欢它那么多。 First off, it sounded like a really neat idea, but then I came to think, that if add syntactic sugar for this kind of thing, people will likely abuse exceptions when a status code is more appropriate. 首先,它听起来像一个非常简洁的想法,但后来我想,如果为这类事情添加语法糖,人们可能会在状态代码更合适时滥用异常。

As a few people have already pointed out, this is already in VB, however you could easily do something similar in C#: 正如一些人已经指出的那样,这已经在VB中了,但你可以在C#中轻松做类似的事情:

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

So, it's really just syntactic sugar. 所以,它真的只是语法糖。

The thing about exceptions is that (as often discussed on this site) they violate the sequential structure of your program, by potentially skipping a lot of code and popping the stack when you really didn't want that. 关于异常的事情是(正如在这个网站上经常讨论的那样)它们违反了程序的顺序结构,可能会跳过很多代码并在你真的不想要的时候弹出堆栈。 This is why I don't think they are good for anything but very exceptional conditions, and if you really can somehow handle these conditions, it should be somewhat of a pain (try{}catch{if(..)} etc.), so that people wont be likely to use exceptions for more than these exceptional conditions. 这就是为什么我不认为它们对任何事情都有好处,只有非常特殊的条件,如果你真的可以以某种方式处理这些条件,那应该有点痛苦(试试{}捕捉{if(..)}等等) ,因此人们不可能使用例外超过这些特殊条件。

Since c# 7.0 this kind of stuff already supported. 由于c#7.0已经支持这种东西。 Here is full reference. 是完整的参考。 Here is code snippet. 这是代码片段。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM