web-dev-qa-db-ja.com

新しい「例外フィルター」機能にはどのような利点がありますか?

C#6には、「例外フィルタリング」と呼ばれる新機能があります

構文は次のとおりです。

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

私は仕方がなく、現在のアプローチに比べてその利点は何であるか疑問に思いました。

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

中括弧の前にフィルタリングが行われるのは大したことですか?おそらくパフォーマンスやセキュリティに関連していますか?

32
stackunderflow

C#6.0の例外フィルター機能には、さまざまな利点があります。ここにいくつかの説明があります(私の認識された重要性によって順序付けられています)

  • 機能のパリティ-例外フィルターは、ILレベルおよびその他の.Net言語(VB.NetおよびF#)で既に実装されています。[1] また、C#およびVB.Net(プロジェクト「Roslyn」)用の新しいコンパイラの構築の一環として、一方の言語に存在し、もう一方の言語にはない多くの機能が実装されました。[2]

  • クラッシュダンプ-例外フィルターはスタックを変更しません。つまり、(クラッシュダンプで)ダンプされた場合、例外が最初にスローされた場所だけでなく、再スローされた場所(実際の問​​題とは無関係)を知ることができます。[3]

  • Debugging-例外がcatchブロックに入ると、_throw;_を使用して再スローされ、スタック内の他の場所では処理されません。 (そして、例外がユーザー処理されない場合、例外設定は中断するように設定されます)デバッガーは、例外が最初にスローされた場所ではなく、_throw;_で中断します(つまり、以下の例では、_throw;_で中断しますthrow new FileNotFoundException();ではありません

  • 複数のcatchブロック-例外フィルターがない場合は、例外をキャッチし、条件を確認し、満たされていない場合は_throw;_ 例外。再スローされた例外は、例外がすべての条件を満たす場合でも、他のcatchブロックを考慮せず、最終的に1つの大きなcatchブロックになります。

    _try
    {
        throw new FileNotFoundException();
    }
    catch (FileNotFoundException e)
    {
        if (!e.FileName.Contains("Hamster"))
        {
            throw;
        }
        // Handle
    }
    catch (Exception e)
    {
        // Unreachable code
    }
    _
  • 読みやすさ-多くの条件で「catchall」catchブロックを使用でき、条件が満たされない場合は_throw;_を使用できます。 (スタックの変更に苦しんでいる間)それぞれが適切な方法で特定の問題を処理する、別個の別個のcatchブロックを持つ方がはるかに明確です。

    _try
    {
    }
    catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00042)  
    { 
    }  
    catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00011)  
    {  
    }
    catch (IOException)
    {  
    }
    _
  • "Abuse"-例外を処理せずに検査する方法として、例外フィルターを使用できます。これは主な利点ではありませんが、素晴らしい副作用です。誤って返すロギングメソッドと空のcatchブロックを使用できます。

    _private static bool LogException(Exception exception)
    {
        Console.WriteLine(exception.Message);
        return false;
    }
    
    try
    {
    }
    catch (ArgumentException exception) when (LogException(exception))  
    {
        // Unreachable code.
    }
    _

結論として、C#6.0の機能のほとんどは小さな改善と構文糖衣構文であり、例外フィルターはそれほど大きな機能ではありませんが、以前は不可能だった機能を提供します。


  1. C#6.0言語プレビュー

    C#6.0でのその他の例外の改善(例外フィルターのサポート)により、言語は他の.NET言語、つまりVisual Basic .NETおよびF#で最新の状態になります。

  2. C#6およびVB 14 の言語機能

  3. C#6の新機能

    例外フィルターは、スタックを無傷のままにするため、キャッチして再スローするよりも望ましいです。後で例外によってスタックがダンプされた場合は、最後に再スローされた場所だけでなく、元の場所からスタックを確認できます。

33
i3arnon

ErenErsönmezはすでに最も重要な利点について言及しています。

例外がキャッチされると、スタックを巻き戻す効果があることを付け加えたいと思います。つまり、throw;を使用して例外を再スローし、スタックの上位でキャッチされない場合、デバッガーは、例外が最初にスローされた場所ではなく、throwを強調表示するため、次のことができます。例外がスローされたときの変数の状態は表示されません。

一方、例外フィルターはnotスタックをアンワインドするため、例外がキャッチされない場合、デバッガーは例外が最初にスローされた場所を表示します。

だからあなたがこれを持っているなら:

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でない場合)。

12
Thomas Levesque

公式から C#機能の説明 (VS2015プレビューのPDFダウンロードリンク):

例外フィルターは、スタックを無傷のままにするため、キャッチして再スローするよりも望ましいです。後で例外によってスタックがダンプされた場合は、最後に再スローされた場所だけでなく、元の場所からスタックを確認できます。

副作用に例外フィルターを使用することも、一般的で受け入れられている「悪用」の形式です。例えばロギング。彼らは、その進路を妨害することなく、「飛んでいる」例外を検査することができます。そのような場合、フィルターは多くの場合、副作用を実行する誤って返すヘルパー関数の呼び出しになります。

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

これにより、条件のチェックが可能になりますなし例外をキャッチします。つまり、条件が満たされない場合、次のキャッチブロックが考慮されます。キャッチして再スローした場合、次のキャッチブロックは考慮されません。

6
Eren Ersönmez

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
}
5
leppie