C# Tips | コレクション・LINQ:キー存在判定

C# C#
スポンサーリンク

はじめに:「キー存在判定」は“ある前提で動くか・ない前提で動くか”を決めるスイッチ

業務システムで Dictionary を使い始めると、必ず出てくる問いがあります。

「このキー、本当にある前提で動いていいのか?」
「なかったらどうする? エラー? スキップ? デフォルト値?」

このときに必ずセットで考えるのが「キー存在判定」です。
C# では主に ContainsKeyTryGetValue を使って、「キーがあるかどうか」を安全に扱えます。

ここから、初心者向けに

  • Dictionary でのキー存在判定の基本
  • ContainsKeyTryGetValue の違いと使い分け
  • 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 には ContainsKeyTryGetValue がある
  • ContainsKey は「あるかどうかだけ」、TryGetValue は「あるかどうか+値取得」
  • List や IEnumerable には LINQ の Any で存在判定を書く
  • 「ないのは異常か/普通か」を先に決めて、例外にするか分岐にするかを選ぶ

ここまで腹落ちしていれば、
「とりあえず dict[key] で書いて、たまに KeyNotFoundException で落ちる」コードから卒業して、
“業務の前提をきちんとコードに刻んだ、堅いキー存在判定”を書けるようになります。

タイトルとURLをコピーしました