例外が発生した際、単に「エラーが発生しました」というメッセージだけでは、原因の特定が困難な場合があります。どの変数がどのような値だったのか、検索条件は何だったのかといった「コンテキスト情報」がなければ、デバッグは難航します。
独自の例外クラスを作成してプロパティを追加する方法もありますが、C#のExceptionクラスには、標準でDataプロパティ(IDictionary)が用意されており、これを利用することで手軽に任意のキーと値を例外に添付することが可能です。
ここでは、在庫検索システムを題材に、検索に失敗した際の検索キーワードやデータ件数を例外情報として保持させる方法について解説します。
Exception.Dataプロパティの概要
ExceptionクラスのDataプロパティは、IDictionaryインターフェースを実装したコレクションです。ここに任意のオブジェクトをキーと値のペアとして格納することで、例外のスロー元からキャッチ先へ、エラーメッセージ以外の付加情報を渡すことができます。
- 利点: わざわざ専用の例外クラス(カスタム例外)を定義することなく、動的な情報を付与できます。
- 用途: ログ出力時に、変数の状態やタイムスタンプなどを記録するのに適しています。
実践的なコード例:在庫検索時の詳細エラー情報
以下のコードは、商品リストから特定の商品コードを検索する処理です。該当する商品が見つからなかった場合に例外をスローしますが、その際に「どんなコードで検索したか」「検索時の在庫数はいくつだったか」をDataプロパティに追加しています。
using System;
using System.Collections; // DictionaryEntryのために必要
using System.Collections.Generic;
using System.Linq;
namespace InventorySystem
{
class Program
{
static void Main()
{
// 在庫データのリスト
var inventory = new List<string>
{
"PROD-001",
"PROD-002",
"PROD-005",
"PROD-010"
};
try
{
// 存在しない商品コードで検索を実行
string targetCode = "PROD-999";
var result = FindProduct(inventory, targetCode);
Console.WriteLine($"商品が見つかりました: {result}");
}
catch (InvalidOperationException ex)
{
// エラーメッセージの出力
Console.WriteLine($"[エラー] {ex.Message}");
// Dataプロパティに含まれる追加情報をすべて出力
// Dataプロパティは IDictionary なので、DictionaryEntry で回します。
Console.WriteLine("--- デバッグ情報 ---");
foreach (DictionaryEntry entry in ex.Data)
{
Console.WriteLine($"Key: {entry.Key,-15} | Value: {entry.Value}");
}
}
}
/// <summary>
/// リストから商品を検索します。見つからない場合は詳細情報付きの例外を投げます。
/// </summary>
static string FindProduct(ICollection<string> inventory, string searchCode)
{
// 部分一致ではなく完全一致で検索
var product = inventory.FirstOrDefault(x => x == searchCode);
if (product == null)
{
// 一般的な例外を作成
var ex = new InvalidOperationException("指定された商品は在庫リストに存在しません。");
// ★ここで独自のデータを追加する
// キーは文字列にするのが一般的です。
ex.Data.Add("SearchCode", searchCode);
ex.Data.Add("InventoryCount", inventory.Count);
ex.Data.Add("Timestamp", DateTime.Now);
// 情報を持たせた例外をスロー
throw ex;
}
return product;
}
}
}
実行結果
[エラー] 指定された商品は在庫リストに存在しません。
--- デバッグ情報 ---
Key: SearchCode | Value: PROD-999
Key: InventoryCount | Value: 4
Key: Timestamp | Value: 2025/12/06 10:00:00
技術的なポイントと注意点
1. キーの競合に注意する
Dataプロパティはキーと値のペアで管理されます。もし、ライブラリ内部や他の箇所ですでに同じキーが使用されていた場合、上書きされるか、追加時にエラーになる可能性があります。キー名には、アプリケーション固有のプレフィックスを付けるなどして、一意性を保つ工夫が推奨されます(例: "MyApp.SearchCode")。
2. シリアライズへの配慮
例外オブジェクトがアプリケーション境界を超えて(例えばWeb APIのレスポンスや分散システム間で)シリアライズされる場合、Dataプロパティに含まれるオブジェクトもシリアライズ可能である必要があります。独自クラスのインスタンスなどを格納する場合は、そのクラスがシリアライズ可能かどうかを確認してください。基本的には、stringやint、DateTimeなどのプリミティブな型を格納するのが安全です。
3. カスタム例外との使い分け
Dataプロパティは「ログ出力のための補足情報」としては優秀ですが、プログラムのロジック制御(条件分岐)に使用するには不向きです。 キャッチ側で「この値が入っていたらリトライする」といった制御を行いたい場合は、Dataプロパティから値を取り出してキャストするよりも、専用のプロパティを持つ「カスタム例外クラス」を作成する方が型安全であり、設計としても適切です。
まとめ
Exception.Dataプロパティを活用することで、既存の例外クラスを拡張することなく、エラー発生時のコンテキスト情報を保持できます。特に運用フェーズにおいて、ログから障害原因を特定する際の強力な手がかりとなるため、例外処理を実装する際は「後で調査するために必要な値」を付与することを検討してください。
