C#6には、「例外フィルタリング」と呼ばれる新機能があります
構文は次のとおりです。
catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00042)
{
//Do something here
}
私は仕方がなく、現在のアプローチに比べてその利点は何であるか疑問に思いました。
catch (Win32Exception exception)
{
if (exception.NativeErrorCode == 0x00042)
{
//Do something here
}
}
中括弧の前にフィルタリングが行われるのは大したことですか?おそらくパフォーマンスやセキュリティに関連していますか?
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の機能のほとんどは小さな改善と構文糖衣構文であり、例外フィルターはそれほど大きな機能ではありませんが、以前は不可能だった機能を提供します。
C#6.0でのその他の例外の改善(例外フィルターのサポート)により、言語は他の.NET言語、つまりVisual Basic .NETおよびF#で最新の状態になります。
C#6およびVB 14 の言語機能
例外フィルターは、スタックを無傷のままにするため、キャッチして再スローするよりも望ましいです。後で例外によってスタックがダンプされた場合は、最後に再スローされた場所だけでなく、元の場所からスタックを確認できます。
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でない場合)。
公式から C#機能の説明 (VS2015プレビューのPDFダウンロードリンク):
例外フィルターは、スタックを無傷のままにするため、キャッチして再スローするよりも望ましいです。後で例外によってスタックがダンプされた場合は、最後に再スローされた場所だけでなく、元の場所からスタックを確認できます。
副作用に例外フィルターを使用することも、一般的で受け入れられている「悪用」の形式です。例えばロギング。彼らは、その進路を妨害することなく、「飛んでいる」例外を検査することができます。そのような場合、フィルターは多くの場合、副作用を実行する誤って返すヘルパー関数の呼び出しになります。
private static bool Log(Exception e) { /* log it */ ; return false; } … try { … } catch (Exception e) if (Log(e)) {}
これにより、条件のチェックが可能になりますなし例外をキャッチします。つまり、条件が満たされない場合、次のキャッチブロックが考慮されます。キャッチして再スローした場合、次のキャッチブロックは考慮されません。
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
}