りおんクロニクル


C# × 例外処理(try/catch・再スロー・ログ)|安全なエラー処理と実務パターン総まとめ【2026年版】

Home【2026年版】C# / .NET入門と実践ガイド|基礎・業務アプリ開発・SQLite連携まで体系的に解説

C# の例外処理は、アプリの品質・安定性を左右する重要な要素です。 しかし、try/catch の書き方を誤ると、 例外が消える・原因が分からない・UIが落ちる・ログが残らない といった問題が発生します。

この記事でわかること
・例外の基本(Exception / InnerException)
・try/catch の正しい書き方
・再スロー(throw)の正しい使い方
・例外を握りつぶす危険性
・業務アプリで使う例外設計
・Serilog で例外ログを残す方法
・UIアプリ(WPF/WinForms)の未処理例外対策

1. 例外の基本(Exception / InnerException)

■ 1-1. 例外は「エラーの詳細情報を持つオブジェクト」

■ 1-2. InnerException は必ず確認する

try
{
    DoWork();
}
catch (Exception ex)
{
    Console.WriteLine(ex.InnerException?.Message);
}

DB・API・ファイルI/O の例外は InnerException に原因が書かれていることが多いです。

2. try/catch の正しい書き方

■ 2-1. 最小限の範囲で try/catch を書く

// 悪い例:広すぎる
try
{
    A();
    B();
    C();
}
catch { }

// 良い例:失敗しやすい箇所だけ
A();
try
{
    B();
}
catch (Exception ex)
{
    Log.Error(ex, "Bでエラー");
}
C();

try/catch は「失敗しやすい箇所」に限定するのが鉄則。

■ 2-2. 例外を握りつぶさない

// 悪い例(絶対NG)
catch (Exception)
{
}

ログも出さずに例外を消すと、原因が永遠に分からなくなります。

3. 再スロー(throw)の正しい使い方

■ 3-1. throw ex; は NG(スタックトレースが消える)

// 悪い例
catch (Exception ex)
{
    throw ex; // スタックトレースがリセットされる
}

■ 3-2. 正しい再スロー

catch
{
    throw; // 元の例外情報を保持したまま再スロー
}

再スローは throw; 一択。

4. 業務アプリでよくある例外処理パターン

■ 4-1. 入力チェック → 業務例外

if (amount <= 0)
    throw new ArgumentException("金額は1以上を指定してください");

■ 4-2. DBエラー → ログを残して上位に投げる

try
{
    await _repo.SaveAsync(order);
}
catch (Exception ex)
{
    Log.Error(ex, "注文保存に失敗");
    throw; // UI層に通知
}

■ 4-3. UI層でユーザー向けメッセージに変換

try
{
    await _service.SaveOrderAsync(order);
}
catch (Exception ex)
{
    MessageBox.Show("保存に失敗しました。ログを確認してください。");
}

「ログは技術者向け」「メッセージはユーザー向け」で分けるのが正解。

5. Serilog で例外ログを残す

■ 5-1. 例外ログの基本

try
{
    DoWork();
}
catch (Exception ex)
{
    Log.Error(ex, "処理中にエラー");
}

Serilog は例外オブジェクトを渡すだけで、 スタックトレースまで自動で記録してくれます。

■ 5-2. 未処理例外をキャッチ(WPF)

AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
    Log.Fatal((Exception)e.ExceptionObject, "未処理例外");
};

業務アプリでは必須の設定。

6. finally の正しい使い方

■ 6-1. リソース解放に使う

try
{
    conn.Open();
    DoWork();
}
finally
{
    conn.Close();
}

finally は「成功しても失敗しても必ず実行される」ブロック。

7. async/await と例外処理の落とし穴

■ 7-1. async void は例外が消える

public async void Save()
{
    throw new Exception("例外"); // 捕まらない
}

■ 7-2. async Task にする

public async Task SaveAsync()
{
    throw new Exception("例外"); // 呼び出し元で捕まる
}

■ 7-3. Task.WhenAll の例外は AggregateException

await Task.WhenAll(task1, task2); // 複数例外に注意

8. 例外を使うべきでないケース

例外は「異常系」にのみ使う。

9. 業務アプリ向けベストプラクティス

まとめ:例外処理は“ログ × 再スロー × UI通知”の3点セットで設計する

「原因が分からないエラーが出る」「ログに何も残らない」 という現場の悩みは、この記事のパターンを押さえるだけで劇的に改善します。 あなたのプロジェクトに合わせて、最適な例外処理設計を組み立ててみてください。

前のページ  次のページ