新的"异常过滤器"有什么好处?功能提供?

时间:2014-11-22 20:19:16

标签: c# exception exception-handling c#-6.0

C#6有一个名为"异常过滤"

的新功能

语法如下:

catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00042)  
{  
    //Do something here 
}  

我无法帮助,但不知道这对当前的方法有什么好处:

catch (Win32Exception exception)   
{
     if (exception.NativeErrorCode == 0x00042)
     {
          //Do something here 
     }
}

在大括号之前进行过滤是否很重要?也许与绩效或安全有关?

5 个答案:

答案 0 :(得分:33)

C#6.0中的异常过滤器功能提供了各种好处。这是对某些人的解释(按我认为的重要性排序)

  • 功能奇偶校验 - 异常过滤器已在IL级别和其他.Net语言(VB.Net& F#) [1] 中实现,作为构建C#和VB.Net的新编译器(项目" Roslyn")的一部分,一种语言中存在的许多功能和另一种语言中缺少的功能都是 [2]

  • 崩溃转储 - 异常过滤器不会修改堆栈。这意味着如果它被转储(在崩溃转储中),您将能够知道最初抛出异常的位置,而不仅仅是重新抛出的位置(这与实际问题无关) [3]

  • 调试 - 当异常进入catch块时,使用throw;重新抛出并且不会在堆栈中的任何其他位置处理(并且异常设置被设置为在用户未处理异常时中断)调试器将在throw;而不是最初抛出异常的地方中断(即在下面的示例中,它将在throw;而不是{{ 1}})

  • 多个throw new FileNotFoundException(); - 如果没有异常过滤器,您必须捕获异常,检查条件以及是否未遇到catch异常。即使异常满足最终导致单个大throw;块的所有条件,重新抛出的异常也不会考虑任何其他catch

    catch
  • 可读性 - 虽然你可以使用"赶上所有" try { throw new FileNotFoundException(); } catch (FileNotFoundException e) { if (!e.FileName.Contains("Hamster")) { throw; } // Handle } catch (Exception e) { // Unreachable code } 阻止了很多条件而catch当它们未被满足时(虽然对堆栈进行了修改),每个句柄都有单独且不同的throw;块更清楚以适当方式解决的具体问题:

    catch
  • "滥用" - 您可以使用异常过滤器来检查异常而不处理异常。虽然这不是一个主要的好处,但它是一个很好的副作用。您可以使用错误返回的日志记录方法和空的try { } catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00042) { } catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00011) { } catch (IOException) { } 块:

    catch

总之,大多数C#6.0功能都是小改进和语法糖,虽然异常过滤器不是一个非常大的功能,它确实提供了以前不可能实现的功能。


  1. A C# 6.0 Language Preview

      

    C#6.0的另一个例外改进 - 支持异常过滤器 - 使语言与其他.NET语言保持同步,即Visual Basic .NET和F#

  2. Languages features in C# 6 and VB 14

  3. New Features in C# 6

      

    异常过滤器比捕获和重新抛出更可取,因为它们可以保持堆栈不受破坏。如果异常稍后导致堆栈被转储,您可以看到它最初的来源,而不仅仅是它重新抛出的最后一个位置。

答案 1 :(得分:12)

ErenErsönmez已经提到了最重要的好处。

我只想补充一点,当捕获异常时,它具有展开堆栈的效果。这意味着如果您使用throw;重新抛出异常并且它没有被捕获到堆栈的更高位置,则调试器将突出显示throw,而不是最初抛出异常的位置,因此您可以'在抛出异常时看到变量的状态。

另一方面,异常过滤器展开堆栈,因此如果未捕获异常,调试器将显示最初抛出异常的位置。

所以如果你有这个:

try
{
    DoSomethingThatThrows();
}
catch (Win32Exception exception)   
{
     if (exception.NativeErrorCode == 0x00042)
     {
          //Do something here 
     }
     else
     {
         throw;
     }
}

throw不是0x42时,调试器将在NativeErrorCode停止。

但如果你有这个:

try
{
    DoSomethingThatThrows();
}
catch (Win32Exception exception) if (exception.NativeErrorCode == 0x00042)
{         
     //Do something here 
}

调试器将在DoSomethingThatThrows中停止(或在其调用的方法中),让您更容易看到导致错误的原因(同样,当NativeErrorCode不是0x42时)

答案 2 :(得分:7)

来自官方C# feature descriptions(PDF下载链接,与VS2015预览一样):

  

异常过滤器比捕获和重新抛出更可取,因为   他们没有受到伤害。如果异常后来导致堆栈   要被倾倒,你可以看到它最初的来源,而不是   只是最后一个地方被重新抛出。

     

使用例外也是一种常见且被接受的“滥用”形式   过滤器的副作用;例如日志记录。他们可以检查异常   “飞过”而没有拦截它的路线。在那些情况下,   filter通常会调用一个错误返回的辅助函数   执行副作用:

private static bool Log(Exception e) { /* log it */ ; return false; }
…
try { … } catch (Exception e) if (Log(e)) {}

答案 3 :(得分:6)

它允许检查条件而不捕获异常,这意味着如果不满足条件,则考虑下一个catch块。如果你抓住并重新投掷,则不会考虑下一个拦截区块。

答案 4 :(得分:5)

真正的利益IMO:

try
{
}
catch (SomeException ex) if (ex.Something)
{
}
catch (Exception ex)
{
  // SomeException with !ex.Something is caught here
  // not the same as calling `throw` for !ex.Something 
  // without filters in previous handler
  // less code duplication
}