c#で例外をThrowするときに注意すること

みなさんこんにちはヒロポンです!

今回はC#でtry{}catch{}を使っていて、VisualStudioに怒られてしまったので、その内容と対応策を書いていきたいと思います。

目次

VisualStudioに怒られた内容

💡 try-catch / using / when 句の使い分けは別記事 C# 例外処理の正解 で詳しく書いてます。

それはコードをある程度書き終わった後の事。

メニュータブからビルド⇒ソリューションでコード分析を実行したところ、以下のようなエラーがたくさん出てきました。

「エラー番号:CA2200 [メソッド名]はキャッチされた例外を再度スローし、その例外を引数として明示的に指定します。最初に例外が発生したスタックの場所を保持するには、’throw’を引数無しで使用してください」

この時に書いていたcatchの内容が以下

try
{
    任意の処理
}
catch(Exception ex)
{
    throw ex;
}

特に問題なさそうなんですが、怒られてしまいました。

CA2200エラーの公式の見解

公式リファレンスは以下になります。
公式リファレンス

全文英語なので、google翻訳にかけてみるとなんとなく重要な部分はここかなーーとおもいます。

例外をスローした元のメソッドと現在のメソッド間のメソッド呼び出しのリストが失われます。元のスタックトレース情報を例外とともに保持throwするには、例外を指定せずにステートメントを使用します。

エラーの内容とこの文言を見てみると、Exeption exという形でエラーの型を指定してしまうと、だめなのかなーーと思います。

CA2200の対策

取り急ぎthrowのみ書け!といわれたので、下記のように修正しました。

try
{
    任意の処理
}
catch
{
    throw;
}

こうすれば怒られなくなりました。

原因はスタックトレースの上書き?

今回怒られた内容私も気になったので、原因について調べてみました。

結果、throw exをすると、スタックトレースが上書きされてしまうようなんですね。

スタックトレースについて調べてみたところ下記のような説明が出てきました。

エラーが発生したときに表示される内容で、そのエラーが発生するまでの過程(どんな処理がどの順番で呼び出されたかの流れ)を、ざっくりと表示したもの

スタックトレースについては以下のサイトがわかりやすいですね。
スタックトレース

この内容を見てみると出てくる結論はひとつ。

throw exをすると、スタックトレースが上書きされるので、エラー内容を見るときに、どこでエラーが発生したのか追跡できなくなるよ!だから、throwは引数無しで実行してね!って事だそうです!

完璧に理解はしていないけれども、なんとなくわかった!って感じですね!

💡 補足: 業務系の現場でよくハマるパターン

俺もこの throw vs throw ex、 業務でハマってきたところを3つ並べておきます。

① throw ex でスタックトレースが消える

catch 内で throw ex すると、 例外発生元の StackTrace が現在地に置き換わる → デバッグ不能。 必ず throw (引数なし) で再 throw する。 これは 業務系本番事故 No.1 の原因。

② 例外を握り潰す catch (Exception) { }

テスト中の楽したくて空 catch を入れる → 本番で原因不明のバグ多発。 catch する場合は最低限ログ出力、 「想定外は throw」が定石。

③ ラップ例外の inner exception を忘れる

カスタム例外を投げる時、 throw new MyException("失敗") だけだと元の例外情報が消える。 throw new MyException("失敗", ex) で inner exception を保持。 ログ追跡可能。

❓ よくある質問

Q1. throw vs throw ex の見分け方は?

A. catch 句内で「再throw」したい時は 必ず throw (引数なし)throw ex はバグ。 ReSharper / Roslyn analyzer がワーニング出す。

Q2. ExceptionDispatchInfo って何?

A. .NET 4.5+ の機能で、 例外を別スレッドで再 throw する時に StackTrace 保持可能。 async / await + catch + 別 thread 再 throw のパターンで使う。

Q3. AggregateException の対処は?

A. Task.WaitAllParallel.ForEach で発生。 .Flatten() で平坦化して inner exception を確認。

Q4. カスタム例外を作るべき場面は?

A. ビジネスロジック固有のエラー (在庫不足 / 権限不足) のみ。 技術エラー (Null参照 / IO失敗) は標準例外を投げる。

Q5. when 句との組み合わせは?

A. catch (SqlException ex) when (ex.Number == 1205) でデッドロックだけリトライ。 catch 内で if 分岐するよりも美しい。 詳細は 例外処理の正解 参照。

📚 関連記事

この記事が気に入ったら
いいねしてね!

どんどんシェア待ってるぜ!!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次