はじめに:「キー存在判定」は“ある前提で動くか・ない前提で動くか”を決めるスイッチ
業務システムで Dictionary を使い始めると、必ず出てくる問いがあります。
「このキー、本当にある前提で動いていいのか?」
「なかったらどうする? エラー? スキップ? デフォルト値?」
このときに必ずセットで考えるのが「キー存在判定」です。
C# では主に ContainsKey と TryGetValue を使って、「キーがあるかどうか」を安全に扱えます。
ここから、初心者向けに
- Dictionary でのキー存在判定の基本
ContainsKeyとTryGetValueの違いと使い分け- LINQ でのキー存在判定(Any)
- 実務での設計の考え方
を、例題付きでかみ砕いて説明していきます。
Dictionary におけるキー存在判定の基本
まずは Dictionary を一つ用意する
例として、商品コード → 商品名 の辞書を用意します。
using System;
using System.Collections.Generic;
var products = new Dictionary<string, string>
{
["A001"] = "りんご",
["B002"] = "バナナ",
["O003"] = "オレンジ",
};
C#ここでの前提は、
- キー:商品コード(string)
- 値:商品名(string)
です。
ContainsKey:とりあえず「あるかどうか」だけ知りたいとき
基本の使い方
「この商品コードが登録されているかどうか」だけ知りたいときは、ContainsKey を使います。
bool exists = products.ContainsKey("A001");
Console.WriteLine(exists); // true
C#存在しないキーの場合は false になります。
bool existsX = products.ContainsKey("X999");
Console.WriteLine(existsX); // false
C#ここでの重要ポイントは、「ContainsKey は“あるかどうかだけ”を返す」ということです。
値そのものは返ってきません。
存在チェックしてから値を取りに行くパターン
よくある書き方がこれです。
if (products.ContainsKey("A001"))
{
var name = products["A001"];
Console.WriteLine(name);
}
else
{
Console.WriteLine("商品が見つかりません");
}
C#ContainsKey で存在を確認してから、products["A001"] で値を取りに行っています。
ここでの重要ポイントは、「dict[key] は“必ずある前提”で使うもの」だということです。
存在しないキーを dict[key] で参照すると、KeyNotFoundException になります。
TryGetValue:存在判定と取得を一度にやる
基本の形
TryGetValue は、「キーがあるかどうか」と「値の取得」を一度にやってくれるメソッドです。
if (products.TryGetValue("A001", out var name))
{
Console.WriteLine(name); // りんご
}
else
{
Console.WriteLine("商品が見つかりません");
}
C#ここでの重要ポイントは、次の2つです。
- 戻り値(bool):キーが存在すれば true、なければ false
out変数:キーが存在すれば値が入り、なければ型のデフォルト値(string なら null)が入る
ContainsKey と違って、「存在判定」と「値の取得」を分けずに書けるのが大きなメリットです。
ContainsKey + インデクサとの違い
同じことを ContainsKey で書くと、こうなります。
if (products.ContainsKey("A001"))
{
var name = products["A001"];
Console.WriteLine(name);
}
C#TryGetValue 版と比べると、
ContainsKey版:内部的に「同じキー検索」を2回やるイメージTryGetValue版:1回の検索で「存在判定+値取得」を済ませる
という違いがあります(パフォーマンス的にも TryGetValue のほうが素直です)。
実務では、「値も欲しいなら TryGetValue」「本当に存在チェックだけなら ContainsKey」という使い分けが定番です。
LINQ でのキー存在判定(Any)
List や IEnumerable に対して「キーがあるか」を調べる
Dictionary ではなく、List<T> や IEnumerable<T> に対して「キーが存在するか」を調べたいときは、LINQ の Any を使います。
社員クラスを例にします。
public class Employee
{
public int No { get; set; }
public string Name { get; set; } = "";
}
C#using System.Linq;
var employees = new List<Employee>
{
new Employee { No = 1, Name = "Sato" },
new Employee { No = 2, Name = "Suzuki" },
};
C#社員番号 2 が存在するかどうかを調べます。
bool exists = employees.Any(e => e.No == 2);
Console.WriteLine(exists); // true
C#ここでの重要ポイントは、「Any(e => 条件) は“条件を満たす要素が1件でもあれば true”」ということです。
Dictionary の ContainsKey に相当する「存在判定」を、List に対して行うイメージです。
Dictionary に対して Any を使うこともできる
Dictionary 自体も IEnumerable<KeyValuePair<TKey, TValue>> なので、Any を使えます。
bool exists = products.Any(kv => kv.Key == "A001");
C#ただし、「キーの存在判定」だけなら、素直に ContainsKey を使ったほうが意図が伝わりやすいです。
実務での設計の考え方
「ないのは異常」か「ないのは普通」かを決める
キー存在判定の設計で一番大事なのは、ここです。
- そのキーが「ない」のは、業務的に異常か?
- それとも、「ない」ことも普通にあり得るか?
「ないのは異常」なら、あえて dict[key] で例外を出す設計もアリです。
// ここでキーがないのは業務的にバグ
var name = products[code];
C#「ないことも普通」なら、TryGetValue で分岐を書くべきです。
if (!products.TryGetValue(code, out var name))
{
// 見つからないときの処理(メッセージ表示・スキップなど)
}
C#ここでの重要ポイントは、「例外を“バグ検知のための仕組み”として使うか、“通常フローで起こり得ること”として扱うか」を最初に決めることです。
「存在チェックだけ」か「値も使う」かでメソッドを選ぶ
整理すると、こうなります。
- 存在チェックだけしたい →
ContainsKey(Dictionary)/Any(List, IEnumerable) - 存在チェック+値を使いたい →
TryGetValue
ContainsKey でチェックしてから dict[key] で取る、という書き方は「できるけど、二度手間」になりがちなので、
値も使うなら TryGetValue を素直に選ぶほうが、読みやすくて効率も良いです。
まとめ:「キー存在判定ユーティリティ」は“安全にデータを信じるための入口”
キー存在判定の本質は、
「このキーを“信じていいかどうか”を、コードとして明示する」
ことです。
押さえておきたいポイントは、
- Dictionary には
ContainsKeyとTryGetValueがある ContainsKeyは「あるかどうかだけ」、TryGetValueは「あるかどうか+値取得」- List や IEnumerable には LINQ の
Anyで存在判定を書く - 「ないのは異常か/普通か」を先に決めて、例外にするか分岐にするかを選ぶ
ここまで腹落ちしていれば、
「とりあえず dict[key] で書いて、たまに KeyNotFoundException で落ちる」コードから卒業して、
“業務の前提をきちんとコードに刻んだ、堅いキー存在判定”を書けるようになります。
