C# ContainsKeyの使い方を基礎から解説|Dictionaryのキー存在確認・TryGetValueとの違いまで

はじめに

C#でDictionaryを使っていると、「指定したキーが存在するか確認してから処理したい」という場面がよくあります。そのときに使う代表的なメソッドがContainsKeyです。

ContainsKeyを使うと、Dictionaryの中に特定のキーが登録されているかどうかを簡単に判定できます。キーが存在しない状態で値を取得しようとするとKeyNotFoundExceptionが発生するため、安全なコードを書くうえで重要なメソッドです。

この記事では、C#のContainsKeyの基本的な使い方から、TryGetValueとの違い、ContainsValueContainsとの使い分け、よくあるエラーの原因と対策まで、初心者にもわかりやすく解説します。

1. C#のContainsKeyとは?Dictionaryでキーの存在確認をする基本

1-1. ContainsKeyの役割と戻り値

ContainsKeyは、Dictionary<TKey, TValue>に指定したキーが存在するかどうかを調べるメソッドです。

戻り値はbool型で、キーが存在する場合はtrue、存在しない場合はfalseを返します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

scores["Alice"] = 90;
scores["Bob"] = 80;

bool exists = scores.ContainsKey("Alice");

Console.WriteLine(exists); // True

この例では、scoresというDictionaryに"Alice"というキーが存在しているため、ContainsKey("Alice")trueを返します。

1-2. Dictionary<TKey,TValue>で使う場面

Dictionary<TKey, TValue>は、キーと値をセットで管理するコレクションです。

たとえば、ユーザー名と点数、商品コードと商品名、設定名と設定値などを扱うときに使われます。

C#
Dictionary<string, string> settings = new Dictionary<string, string>();

settings["Theme"] = "Dark";
settings["Language"] = "Japanese";

このようなDictionaryでは、特定のキーが存在するか確認してから値を取得したり、追加・更新の処理を切り替えたりすることがあります。そのような場面でContainsKeyが役立ちます。

1-3. ContainsKeyで確認できる「キー」と「値」の違い

ContainsKeyで確認できるのは、Dictionaryの「キー」です。

C#
Dictionary<string, string> users = new Dictionary<string, string>();

users["u001"] = "Tanaka";
users["u002"] = "Suzuki";

この場合、"u001""u002"がキーで、"Tanaka""Suzuki"が値です。

C#
Console.WriteLine(users.ContainsKey("u001"));   // True
Console.WriteLine(users.ContainsKey("Tanaka")); // False

"Tanaka"は値であってキーではないため、ContainsKey("Tanaka")falseになります。

値の存在を確認したい場合は、ContainsValueを使います。

1-4. ContainsKeyが必要になる代表的なケース

ContainsKeyは、主に次のような場面で使われます。

Dictionaryから安全に値を取得したいとき、同じキーを重複して追加しないようにしたいとき、キーが存在する場合だけ値を更新したいとき、キーが存在しない場合だけ初期値を登録したいときなどです。

特に初心者がよく遭遇するのが、存在しないキーを指定して値を取得し、KeyNotFoundExceptionが発生するケースです。ContainsKeyを使えば、このようなエラーを事前に防げます。

2. ContainsKeyの基本的な使い方

2-1. Dictionaryを作成する基本コード

まずは、Dictionaryを作成する基本コードを確認しましょう。

C#
Dictionary<string, int> ages = new Dictionary<string, int>();

ages.Add("Alice", 25);
ages.Add("Bob", 30);
ages.Add("Charlie", 28);

このDictionaryでは、名前をキー、年齢を値として管理しています。

また、次のように初期化と同時にデータを入れることもできます。

C#
Dictionary<string, int> ages = new Dictionary<string, int>
{
{ "Alice", 25 },
{ "Bob", 30 },
{ "Charlie", 28 }
};

どちらの書き方でも、ContainsKeyの使い方は同じです。

2-2. ContainsKeyでキーが存在するか判定する書き方

ContainsKeyは、次のように書きます。

C#
dictionary.ContainsKey(キー);

具体例は次のとおりです。

C#
Dictionary<string, int> ages = new Dictionary<string, int>
{
{ "Alice", 25 },
{ "Bob", 30 }
};

bool hasAlice = ages.ContainsKey("Alice");
bool hasDavid = ages.ContainsKey("David");

Console.WriteLine(hasAlice); // True
Console.WriteLine(hasDavid); // False

"Alice"はDictionaryに存在するためtrue"David"は存在しないためfalseになります。

2-3. if文と組み合わせた実用例

ContainsKeyは、単体で使うよりもif文と組み合わせて使うことが多いです。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 },
{ "Bob", 80 }
};

if (scores.ContainsKey("Alice"))
{
Console.WriteLine("Aliceの点数は登録されています。");
}
else
{
Console.WriteLine("Aliceの点数は登録されていません。");
}

実行結果は次のようになります。

C#
Aliceの点数は登録されています。

このように、キーの有無によって処理を分けることができます。

2-4. キーが存在しない場合の処理例

キーが存在しない場合に、新しく追加する処理もよく使われます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 }
};

if (!scores.ContainsKey("Bob"))
{
scores.Add("Bob", 80);
}

Console.WriteLine(scores["Bob"]); // 80

!scores.ContainsKey("Bob")は、「Bobというキーが存在しない場合」という意味です。

存在しないことを確認してからAddすることで、同じキーを重複して追加するエラーを防げます。

2-5. 文字列キー・数値キーでの使用例

Dictionaryのキーには、文字列だけでなく数値も使えます。

文字列キーの例です。

C#
Dictionary<string, string> products = new Dictionary<string, string>
{
{ "A001", "ノートPC" },
{ "A002", "マウス" }
};

if (products.ContainsKey("A001"))
{
Console.WriteLine(products["A001"]);
}

数値キーの例です。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Tanaka" },
{ 2, "Suzuki" }
};

if (users.ContainsKey(1))
{
Console.WriteLine(users[1]);
}

文字列キーでも数値キーでも、基本的な使い方は変わりません。

3. ContainsKeyを使ってDictionaryの値を安全に取得する方法

3-1. 存在確認せずに値を取得すると起きるエラー

Dictionaryでは、存在しないキーを指定して値を取得しようとするとエラーになります。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 }
};

Console.WriteLine(scores["Bob"]);

このコードでは、"Bob"というキーがDictionaryに存在しないため、KeyNotFoundExceptionが発生します。

Dictionaryから値を取得するときは、キーが存在するかどうかを事前に確認することが大切です。

3-2. KeyNotFoundExceptionを防ぐ書き方

KeyNotFoundExceptionを防ぐには、ContainsKeyでキーの存在を確認してから値を取得します。

C#
if (scores.ContainsKey("Bob"))
{
Console.WriteLine(scores["Bob"]);
}
else
{
Console.WriteLine("Bobの点数は登録されていません。");
}

このように書けば、キーが存在しない場合でも安全に処理できます。

3-3. ContainsKey後に値を取得するサンプルコード

実際によく使う形は次のようになります。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 },
{ "Bob", 80 }
};

string name = "Alice";

if (scores.ContainsKey(name))
{
int score = scores[name];
Console.WriteLine($"{name}の点数は{score}点です。");
}
else
{
Console.WriteLine($"{name}の点数は見つかりませんでした。");
}

このコードでは、変数nameに入っているキーを使ってDictionaryを検索しています。キーが存在する場合だけ値を取得するため、安全に実行できます。

3-4. 値の更新・追加処理での活用例

ContainsKeyは、値の更新や追加処理にも使えます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 }
};

string name = "Alice";
int newScore = 95;

if (scores.ContainsKey(name))
{
scores[name] = newScore;
}
else
{
scores.Add(name, newScore);
}

Console.WriteLine(scores[name]); // 95

キーが存在する場合は値を更新し、存在しない場合は新しく追加しています。

ただし、このような「存在すれば更新、なければ追加」という処理だけであれば、次のようにインデクサを使ってシンプルに書くこともできます。

C#
scores[name] = newScore;

Dictionaryのインデクサに代入する場合、キーが存在すれば更新され、存在しなければ追加されます。

4. ContainsKeyとTryGetValueの違い

4-1. TryGetValueとは何か

TryGetValueは、Dictionaryに指定したキーが存在するか確認しながら、同時に値も取得できるメソッドです。

C#
bool result = dictionary.TryGetValue(キー, out );

キーが存在する場合はtrueを返し、対応する値がout引数に入ります。キーが存在しない場合はfalseを返します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 },
{ "Bob", 80 }
};

if (scores.TryGetValue("Alice", out int score))
{
Console.WriteLine(score); // 90
}

4-2. ContainsKeyとTryGetValueの書き方の違い

ContainsKeyを使って値を取得する場合は、存在確認と値の取得を分けて書きます。

C#
if (scores.ContainsKey("Alice"))
{
int score = scores["Alice"];
Console.WriteLine(score);
}

一方、TryGetValueを使う場合は、存在確認と値の取得を同時に行えます。

C#
if (scores.TryGetValue("Alice", out int score))
{
Console.WriteLine(score);
}

どちらも安全に値を取得できますが、値の取得まで行うならTryGetValueのほうが簡潔です。

4-3. 値を取得するならTryGetValueが向いている理由

値を取得したい場合は、TryGetValueが向いています。

理由は、Dictionary内の検索を1回で済ませられるからです。

ContainsKeyで存在確認したあとにdictionary[key]で値を取得すると、内部的にはキー検索が2回行われる可能性があります。

C#
if (scores.ContainsKey("Alice"))
{
int score = scores["Alice"];
}

これに対して、TryGetValueは検索と取得をまとめて行います。

C#
if (scores.TryGetValue("Alice", out int score))
{
Console.WriteLine(score);
}

そのため、値を取得することが目的なら、基本的にはTryGetValueを使うのがおすすめです。

4-4. ContainsKeyが向いているケース

ContainsKeyが向いているのは、値を取得する必要がなく、キーの存在だけを確認したい場合です。

たとえば、次のようなケースです。

C#
if (registeredUsers.ContainsKey(userId))
{
Console.WriteLine("このユーザーIDはすでに登録されています。");
}

この例では、値そのものは必要ありません。キーが存在するかどうかだけ分かればよいため、ContainsKeyで十分です。

また、キーの有無によって追加処理を分けたい場合にも使いやすいです。

C#
if (!cache.ContainsKey(key))
{
cache[key] = CreateValue();
}

4-5. パフォーマンス面での違い

パフォーマンス面では、値を取得するならTryGetValueのほうが効率的です。

ContainsKeyとインデクサによる取得を組み合わせると、キーの検索が重複することがあります。

C#
if (dict.ContainsKey(key))
{
var value = dict[key];
}

一方、TryGetValueは1回の検索で存在確認と値の取得を行います。

C#
if (dict.TryGetValue(key, out var value))
{
// valueを使う
}

小さなDictionaryでは大きな差にならないことも多いですが、大量データや頻繁に呼び出される処理では、TryGetValueを選ぶほうが無駄を減らせます。

4-6. どちらを使うべきかの判断基準

ContainsKeyTryGetValueは、目的によって使い分けるのが基本です。

キーが存在するかどうかだけ知りたい場合はContainsKey、存在確認と同時に値も取得したい場合はTryGetValueを使いましょう。

たとえば、次のような判断です。

C#
// キーの存在だけ確認したい
if (dict.ContainsKey(key))
{
Console.WriteLine("キーがあります");
}

// 値も取得したい
if (dict.TryGetValue(key, out var value))
{
Console.WriteLine(value);
}

初心者のうちは、「値を使うならTryGetValue、キーの有無だけならContainsKey」と覚えておくとよいでしょう。

5. ContainsKeyとContainsValue・Containsの違い

5-1. ContainsValueは値の存在確認に使う

ContainsValueは、Dictionaryに指定した値が存在するかどうかを確認するメソッドです。

C#
Dictionary<string, string> users = new Dictionary<string, string>
{
{ "u001", "Tanaka" },
{ "u002", "Suzuki" }
};

Console.WriteLine(users.ContainsValue("Tanaka")); // True
Console.WriteLine(users.ContainsValue("Sato")); // False

ContainsKeyがキーを調べるのに対して、ContainsValueは値を調べます。

5-2. ContainsKeyとContainsValueの使い分け

ContainsKeyContainsValueは、調べる対象が異なります。

キーの存在を確認したい場合はContainsKeyを使います。

C#
users.ContainsKey("u001");

値の存在を確認したい場合はContainsValueを使います。

C#
users.ContainsValue("Tanaka");

Dictionaryはキーによる検索を高速に行うためのコレクションです。そのため、通常はContainsKeyのほうがよく使われます。

一方、ContainsValueは値を順番に探す必要があるため、データ数が多い場合はContainsKeyより遅くなる可能性があります。

5-3. DictionaryにおけるContainsの扱い

Dictionaryには、ContainsKeyContainsValueのほかに、Containsというメソッドが使える場合があります。

ただし、DictionaryでContainsを使う場合は、キーだけではなくKeyValuePair<TKey, TValue>、つまりキーと値のペアを対象にする点に注意が必要です。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 }
};

bool result = scores.Contains(new KeyValuePair<string, int>("Alice", 90));

Console.WriteLine(result); // True

キーの存在確認をしたいだけなら、ContainsではなくContainsKeyを使うのがわかりやすく安全です。

5-4. Listや配列のContainsとの違い

Listや配列のContainsは、要素そのものが含まれているかを確認します。

C#
List<string> names = new List<string> { "Alice", "Bob" };

Console.WriteLine(names.Contains("Alice")); // True

配列でも同じように使えます。

C#
string[] names = { "Alice", "Bob" };

Console.WriteLine(names.Contains("Bob")); // True

一方、Dictionaryはキーと値のペアを管理するコレクションです。そのため、キーの存在確認にはContainsKey、値の存在確認にはContainsValueを使うのが基本です。

6. ContainsKeyを使うときの注意点

6-1. 大文字・小文字の違いに注意する

文字列をキーにする場合、大文字と小文字は通常区別されます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 }
};

Console.WriteLine(scores.ContainsKey("Alice")); // True
Console.WriteLine(scores.ContainsKey("alice")); // False

"Alice""alice"は別のキーとして扱われるため、意図せずfalseになることがあります。

大文字・小文字を区別したくない場合は、Dictionary作成時に比較方法を指定します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>(
StringComparer.OrdinalIgnoreCase
);

scores["Alice"] = 90;

Console.WriteLine(scores.ContainsKey("alice")); // True

このようにStringComparer.OrdinalIgnoreCaseを指定すると、大文字・小文字を無視してキーを比較できます。

6-2. nullキーを扱う場合の注意点

Dictionary<TKey, TValue>では、通常、キーにnullを指定すると例外が発生します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

scores.ContainsKey(null); // ArgumentNullException

文字列キーのDictionaryであっても、nullをキーとして扱うことはできません。

そのため、キーに渡す値がnullになる可能性がある場合は、事前に確認しましょう。

C#
string key = null;

if (key != null && scores.ContainsKey(key))
{
Console.WriteLine(scores[key]);
}

6-3. 同じキーを追加しようとした場合のエラー

Dictionaryに同じキーをAddで追加しようとすると、例外が発生します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

scores.Add("Alice", 90);
scores.Add("Alice", 95); // エラー

このようなエラーを防ぐには、ContainsKeyで存在確認してから追加します。

C#
if (!scores.ContainsKey("Alice"))
{
scores.Add("Alice", 90);
}

すでに存在する場合は更新したいのであれば、インデクサを使います。

C#
scores["Alice"] = 95;

この書き方なら、キーが存在すれば更新、存在しなければ追加になります。

6-4. キーの型と比較方法を理解する

Dictionaryのキー比較は、キーの型に応じた比較方法で行われます。

たとえば、intキーでは数値として比較され、stringキーでは文字列として比較されます。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Tanaka" }
};

Console.WriteLine(users.ContainsKey(1)); // True

一方、文字列キーでは空白や大文字小文字の違いにも注意が必要です。

C#
Dictionary<string, string> dict = new Dictionary<string, string>
{
{ "user001", "Tanaka" }
};

Console.WriteLine(dict.ContainsKey("user001")); // True
Console.WriteLine(dict.ContainsKey("user001 ")); // False

見た目が似ていても、末尾に空白が入っていると別のキーとして扱われます。

6-5. カスタムクラスをキーにする場合の注意点

カスタムクラスをDictionaryのキーにする場合は、等価比較の仕組みを正しく実装する必要があります。

たとえば、次のようなクラスをキーにしたとします。

C#
class UserKey
{
public int Id { get; set; }
}

このクラスをそのままキーにすると、同じIdを持っていても別インスタンスであれば別のキーとして扱われることがあります。

C#
Dictionary<UserKey, string> users = new Dictionary<UserKey, string>();

var key1 = new UserKey { Id = 1 };
var key2 = new UserKey { Id = 1 };

users[key1] = "Tanaka";

Console.WriteLine(users.ContainsKey(key2)); // Falseになる可能性がある

同じ内容のオブジェクトを同じキーとして扱いたい場合は、EqualsGetHashCodeを適切に実装する必要があります。

C#
class UserKey
{
public int Id { get; set; }

public override bool Equals(object obj)
{
return obj is UserKey other && Id == other.Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}
}

カスタムクラスをキーにする場合は、この点を理解しておかないと、ContainsKeyが期待どおりに動かないことがあります。

7. ContainsKeyの実践的な活用例

7-1. 設定値が存在するか確認する例

アプリケーションの設定値をDictionaryで管理する場合、必要な設定が存在するか確認できます。

C#
Dictionary<string, string> config = new Dictionary<string, string>
{
{ "Theme", "Dark" },
{ "Language", "Japanese" }
};

if (config.ContainsKey("Theme"))
{
Console.WriteLine($"テーマ: {config["Theme"]}");
}
else
{
Console.WriteLine("テーマ設定が見つかりません。");
}

設定ファイルや環境変数から読み込んだ値を管理するときにも、このような書き方が役立ちます。

7-2. ユーザーIDや商品コードをキーにした検索例

ユーザーIDや商品コードをキーにすると、特定のデータを素早く検索できます。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1001, "Tanaka" },
{ 1002, "Suzuki" }
};

int userId = 1001;

if (users.ContainsKey(userId))
{
Console.WriteLine($"ユーザー名: {users[userId]}");
}
else
{
Console.WriteLine("ユーザーが見つかりません。");
}

商品コードをキーにする例です。

C#
Dictionary<string, string> products = new Dictionary<string, string>
{
{ "P001", "キーボード" },
{ "P002", "マウス" }
};

string productCode = "P002";

if (products.ContainsKey(productCode))
{
Console.WriteLine($"商品名: {products[productCode]}");
}

7-3. カウント処理でキーの有無を判定する例

文字列の出現回数を数える処理でも、ContainsKeyはよく使われます。

C#
string[] words = { "apple", "banana", "apple", "orange", "banana", "apple" };

Dictionary<string, int> counts = new Dictionary<string, int>();

foreach (string word in words)
{
if (counts.ContainsKey(word))
{
counts[word]++;
}
else
{
counts[word] = 1;
}
}

foreach (var item in counts)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

実行結果は次のようになります。

C#
apple: 3
banana: 2
orange: 1

キーがすでに存在する場合はカウントを増やし、存在しない場合は初期値として1を設定しています。

7-4. キャッシュ処理でContainsKeyを使う例

一度取得したデータをDictionaryに保存しておき、次回以降は再利用するようなキャッシュ処理にもContainsKeyを使えます。

C#
Dictionary<string, string> cache = new Dictionary<string, string>();

string GetData(string key)
{
if (cache.ContainsKey(key))
{
return cache[key];
}

string data = $"取得したデータ: {key}";
cache[key] = data;

return data;
}

この例では、キャッシュにキーが存在する場合は保存済みの値を返し、存在しない場合は新しくデータを作成して保存しています。

ただし、値も取得する場合はTryGetValueを使うとより効率的です。

C#
string GetData(string key)
{
if (cache.TryGetValue(key, out string data))
{
return data;
}

data = $"取得したデータ: {key}";
cache[key] = data;

return data;
}

7-5. 入力値のバリデーションに使う例

ユーザーの入力値が許可された値かどうかを確認する用途にも使えます。

C#
Dictionary<string, string> roles = new Dictionary<string, string>
{
{ "admin", "管理者" },
{ "user", "一般ユーザー" },
{ "guest", "ゲスト" }
};

string inputRole = "admin";

if (roles.ContainsKey(inputRole))
{
Console.WriteLine("有効な権限です。");
}
else
{
Console.WriteLine("無効な権限です。");
}

このように、Dictionaryを許可リストのように使うことで、入力値のチェックを簡単に行えます。

8. ContainsKeyのパフォーマンスと内部動作

8-1. Dictionaryの検索が高速な理由

Dictionaryの検索が高速な理由は、内部的にハッシュテーブルの仕組みを使っているためです。

キーからハッシュ値を計算し、そのハッシュ値をもとにデータの格納場所を探します。これにより、Listのように先頭から順番に探すよりも高速にキーを検索できます。

たとえば、Listで特定の要素を探す場合、データ数が増えるほど検索に時間がかかりやすくなります。一方、Dictionaryはキーによる検索に強く、データ数が多い場合でも効率よくアクセスできます。

8-2. ContainsKeyの計算量

ContainsKeyの平均的な計算量は、一般的にO(1)です。

O(1)とは、データ数が増えても検索にかかる時間が大きく増えにくいことを意味します。

ただし、ハッシュ値の衝突が多い場合など、状況によっては効率が落ちることもあります。それでも通常の使い方では、Dictionaryのキー検索は非常に高速です。

8-3. 大量データで使う場合のポイント

大量データを扱う場合は、キーの設計が重要です。

キーには、重複しない値を使う必要があります。また、文字列キーを使う場合は、大文字小文字や空白の違いで別キーにならないよう注意しましょう。

C#
string key = input.Trim();

入力値をキーにする場合は、必要に応じてTrimで前後の空白を削除したり、比較方法を統一したりすると、意図しない検索失敗を防げます。

大文字小文字を無視したい場合は、Dictionary作成時にStringComparer.OrdinalIgnoreCaseを指定するのが有効です。

C#
Dictionary<string, int> dict = new Dictionary<string, int>(
StringComparer.OrdinalIgnoreCase
);

8-4. ContainsKeyを何度も呼ぶときの注意点

ContainsKeyは高速ですが、必要以上に何度も呼ぶと無駄な処理になります。

たとえば、同じキーに対して何度も存在確認するコードは避けたほうがよいです。

C#
if (dict.ContainsKey(key))
{
Console.WriteLine(dict[key]);
}

if (dict.ContainsKey(key))
{
// 別の処理
}

このような場合は、結果を変数に入れて使い回すか、値も必要ならTryGetValueを使うとよいでしょう。

C#
if (dict.TryGetValue(key, out var value))
{
Console.WriteLine(value);
// valueを使った別の処理
}

8-5. TryGetValueで無駄な検索を減らす考え方

ContainsKeyで存在確認してから値を取得すると、検索が重複する可能性があります。

C#
if (dict.ContainsKey(key))
{
var value = dict[key];
}

値を取得することが目的なら、次のようにTryGetValueを使うと検索を1回にまとめられます。

C#
if (dict.TryGetValue(key, out var value))
{
Console.WriteLine(value);
}

実務では、単にキーの存在だけを確認するならContainsKey、値も使うならTryGetValueという考え方で使い分けると、読みやすく効率のよいコードになります。

9. ContainsKeyでよくあるエラーと解決方法

9-1. KeyNotFoundExceptionが発生する原因

KeyNotFoundExceptionは、存在しないキーを使ってDictionaryから値を取得しようとしたときに発生します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

scores["Alice"] = 90;

Console.WriteLine(scores["Bob"]); // KeyNotFoundException

解決方法は、ContainsKeyまたはTryGetValueで存在確認してから値を取得することです。

C#
if (scores.ContainsKey("Bob"))
{
Console.WriteLine(scores["Bob"]);
}
else
{
Console.WriteLine("Bobは存在しません。");
}

値も同時に取得する場合は、TryGetValueが便利です。

C#
if (scores.TryGetValue("Bob", out int score))
{
Console.WriteLine(score);
}
else
{
Console.WriteLine("Bobは存在しません。");
}

9-2. ArgumentNullExceptionが発生する原因

ArgumentNullExceptionは、ContainsKeynullを渡した場合などに発生します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

string key = null;

scores.ContainsKey(key); // ArgumentNullException

解決するには、ContainsKeyを呼ぶ前にnullチェックを行います。

C#
if (key != null && scores.ContainsKey(key))
{
Console.WriteLine(scores[key]);
}

ユーザー入力や外部データをキーにする場合は、nullが入る可能性を考慮しておきましょう。

9-3. キーがあるはずなのにfalseになる原因

キーが存在するはずなのにContainsKeyfalseになる場合、次のような原因が考えられます。

大文字小文字が違う、前後に空白が入っている、全角半角が違う、別の型のキーを使っている、カスタムクラスの比較処理が正しくない、といったケースです。

たとえば、次のコードでは末尾に空白があるためfalseになります。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 }
};

Console.WriteLine(scores.ContainsKey("Alice ")); // False

解決するには、キーを登録するときと検索するときの形式を統一することが大切です。

C#
string key = input.Trim();

if (scores.ContainsKey(key))
{
Console.WriteLine(scores[key]);
}

9-4. 大文字小文字の違いで判定に失敗する場合

文字列キーでは、大文字小文字の違いによって判定に失敗することがあります。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 }
};

Console.WriteLine(scores.ContainsKey("alice")); // False

大文字小文字を区別したくない場合は、Dictionary作成時に比較方法を指定します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>(
StringComparer.OrdinalIgnoreCase
);

scores["Alice"] = 90;

Console.WriteLine(scores.ContainsKey("alice")); // True

後から比較方法だけを変更することはできないため、大文字小文字を無視したい場合はDictionaryを作成する時点で指定しましょう。

9-5. カスタムオブジェクトの比較で判定に失敗する場合

カスタムクラスをキーにした場合、同じ内容のオブジェクトでも別のキーとして扱われることがあります。

C#
class ProductKey
{
public string Code { get; set; }
}
C#
Dictionary<ProductKey, string> products = new Dictionary<ProductKey, string>();

var key1 = new ProductKey { Code = "P001" };
var key2 = new ProductKey { Code = "P001" };

products[key1] = "キーボード";

Console.WriteLine(products.ContainsKey(key2)); // Falseになる可能性がある

この問題を解決するには、EqualsGetHashCodeを実装します。

C#
class ProductKey
{
public string Code { get; set; }

public override bool Equals(object obj)
{
return obj is ProductKey other && Code == other.Code;
}

public override int GetHashCode()
{
return Code?.GetHashCode() ?? 0;
}
}

カスタムオブジェクトをDictionaryのキーにする場合は、比較方法を明確に定義することが重要です。

10. ContainsKeyに関するよくある質問

10-1. ContainsKeyは値の存在確認にも使える?

ContainsKeyは値の存在確認には使えません。

ContainsKeyで確認できるのはキーだけです。

C#
Dictionary<string, string> users = new Dictionary<string, string>
{
{ "u001", "Tanaka" }
};

Console.WriteLine(users.ContainsKey("u001")); // True
Console.WriteLine(users.ContainsKey("Tanaka")); // False

値の存在を確認したい場合は、ContainsValueを使います。

C#
Console.WriteLine(users.ContainsValue("Tanaka")); // True

10-2. ContainsKeyとTryGetValueはどちらが速い?

キーの存在確認だけであれば、ContainsKeyで問題ありません。

しかし、存在確認後に値も取得する場合は、TryGetValueのほうが効率的です。

C#
if (dict.TryGetValue(key, out var value))
{
Console.WriteLine(value);
}

ContainsKeyのあとにdict[key]で値を取得すると、検索が重複する可能性があるためです。

10-3. ContainsKeyで大文字小文字を無視できる?

Dictionaryを作成するときにStringComparer.OrdinalIgnoreCaseを指定すれば、大文字小文字を無視できます。

C#
Dictionary<string, int> dict = new Dictionary<string, int>(
StringComparer.OrdinalIgnoreCase
);

dict["Apple"] = 100;

Console.WriteLine(dict.ContainsKey("apple")); // True
Console.WriteLine(dict.ContainsKey("APPLE")); // True

注意点として、Dictionaryを作成したあとに比較方法を変更することはできません。大文字小文字を無視したい場合は、最初にDictionaryを作る段階で指定しましょう。

10-4. Dictionary以外でもContainsKeyは使える?

ContainsKeyは、主にキーと値のペアを扱うコレクションで使われます。

代表的なのはDictionary<TKey, TValue>です。そのほか、SortedDictionary<TKey, TValue>SortedList<TKey, TValue>など、キーを持つコレクションでも使えます。

一方、List<T>や配列にはContainsKeyはありません。Listや配列では、要素の存在確認にContainsを使います。

C#
List<string> names = new List<string> { "Alice", "Bob" };

Console.WriteLine(names.Contains("Alice")); // True

10-5. ContainsKeyを使わずにキー存在確認する方法はある?

ContainsKeyを使わずにキーの存在確認をする方法として、TryGetValueがあります。

C#
if (dict.TryGetValue(key, out var value))
{
Console.WriteLine("キーが存在します");
}

値も必要な場合は、TryGetValueを使うのがおすすめです。

また、キーが存在しない場合に値を追加したいだけなら、インデクサやTryAddを使う方法もあります。

C#
dict[key] = value;

この書き方は、キーが存在すれば更新、存在しなければ追加になります。

重複を避けて追加したい場合は、TryAddも便利です。

C#
bool added = dict.TryAdd(key, value);

if (added)
{
Console.WriteLine("追加しました");
}
else
{
Console.WriteLine("すでにキーが存在します");
}

まとめ

C#のContainsKeyは、Dictionary<TKey, TValue>に指定したキーが存在するか確認するためのメソッドです。

戻り値はbool型で、キーが存在すればtrue、存在しなければfalseを返します。存在しないキーを使って値を取得するとKeyNotFoundExceptionが発生するため、事前にContainsKeyで確認することで安全なコードを書けます。

ただし、値も取得したい場合はTryGetValueのほうが向いています。ContainsKeyはキーの存在確認、TryGetValueは存在確認と値の取得を同時に行いたいときに使うとよいでしょう。

また、値の存在確認にはContainsValue、Listや配列の要素確認にはContainsを使います。それぞれ役割が異なるため、目的に応じて正しく使い分けることが大切です。

ContainsKeyを使う際は、大文字小文字の違い、nullキー、重複キー、カスタムクラスの比較方法などにも注意しましょう。基本を理解しておけば、Dictionaryを使った検索・追加・更新処理をより安全で読みやすく実装できます。