Dictionary から値を引いたら KeyNotFoundException(「指定されたキーはディレクトリ内に存在しませんでした」)で落ちる。Null 系の例外と違い、これは「器はあるがキーが無い」状態で、原因はキーの登録漏れか、キー自体の作り間違いのどちらかにほぼ絞れる。
先に結論
- 発生条件はシンプルで、存在しないキーでインデクサ(
dict[key])を引いたときだけ - 取り方が分かれる: 在る前提ならインデクサ、無いかもしれないなら
TryGetValue を使う - バグの多くは「登録したつもり」: 初期化漏れ、キー生成の不整合(大文字小文字・型違い)に潜む
ContainsKey で確認してから引く書き方は、検索が2回走るので原則 TryGetValue に寄せる
よくある発生パターン
- 初期化・登録処理を通っていないキーで引く(条件分岐で登録がスキップされる)
- キーの作りが登録側と参照側で違う: 文字列の大文字小文字、末尾スペース、数値キーと文字列キーの取り違え
- 外部入力(JSON・CSV・設定ファイル)を Dictionary 化した前提が崩れている
- 列挙中にキーが変わる設計で、古いキーのまま引き続ける
安全な引き方
「無ければ既定値・分岐」で済むなら TryGetValue が基本形。1回の検索で有無と値が取れる。
if (dict.TryGetValue(key, out var value))
{
// 在ったときの処理
}
// 無ければ既定値でよい場合
var v = dict.GetValueOrDefault(key, defaultValue);
ContainsKey + インデクサの組み合わせは検索2回分。意図が無ければ置き換える- 「必ず在るべきキー」なら、引く側で握りつぶさず、登録側の漏れを直すのが本筋
- 設定値のようなものは起動時にまとめて検証し、実行時に落ちる形を避ける
キーが合わない系の落とし穴
- 文字列キーは大文字小文字を区別する(StringComparer.OrdinalIgnoreCase を渡した Dictionary は別)
- 独自クラスをキーにする場合、Equals / GetHashCode の実装次第で「同じはずのキー」が一致しない
- 浮動小数点数や日時をキーにすると、表現の揺れで一致しない事故が起きやすい
予防
- キーの生成ロジックを1箇所に寄せ、登録側と参照側で別々に組み立てない
- 外部入力由来の Dictionary は、引く前に「想定キーが揃っているか」を検証する
- テストでは「キーが無いケース」を必ず通す。正常系だけだとこの例外は潜伏する
ざっくりまとめ
- KeyNotFoundException は「キーが無い」ただそれだけ。在る前提か無い前提かをコードで選ぶ
- 無い前提なら TryGetValue / GetValueOrDefault、在る前提なら登録漏れを直す
- キーの不整合(大小文字・型・独自クラスの等価性)が隠れ原因の定番
関連記事