C# Dictionaryをforeachでループ処理する方法|Key・Valueの取得から注意点まで解説

はじめに

C#でDictionaryを扱うとき、よく使う処理のひとつがforeachによるループ処理です。

Dictionaryは、キーと値をセットで管理できる便利なコレクションです。たとえば、ユーザーIDとユーザー名、商品コードと価格、都道府県名と人口のように、「何かをキーにして値を取り出したい」場面でよく使われます。

この記事では、C#のDictionaryforeachでループ処理する基本から、KeyValueの取得方法、KeysValuesだけを回す方法、ループ中に変更するときの注意点、よくあるエラーまで解説します。

1. C#のDictionaryをforeachでループ処理する基本

1-1. DictionaryとはKeyとValueをセットで管理するコレクション

Dictionary<TKey, TValue>は、KeyValueを1組として管理するコレクションです。

たとえば、次のように文字列のキーと数値の値をセットで登録できます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

この例では、"田中""佐藤"Key8095Valueです。

Dictionaryでは、キーを指定して値を取得できます。

C#
Console.WriteLine(scores["佐藤"]);

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

C#
95

このように、Dictionaryは「キーから値をすばやく取り出す」ためによく使われます。

1-2. foreachでDictionaryをループできる理由

Dictionaryは複数の要素を持つコレクションなので、foreachで順番に取り出して処理できます。

Dictionaryforeachで回すと、1回のループでKeyValueのセットが取り出されます。

C#
foreach (var item in scores)
{
Console.WriteLine(item.Key + ":" + item.Value);
}

Dictionaryの各要素は、単なる値ではなく、KeyValuePair<TKey, TValue>という形式で取り出されます。

そのため、item.Keyでキーを取得し、item.Valueで値を取得できます。

1-3. Dictionaryの基本的なforeach構文

Dictionaryforeachでループする基本構文は次のとおりです。

C#
foreach (KeyValuePair<TKey, TValue> item in dictionary)
{
// item.Key でキーを取得
// item.Value で値を取得
}

実際のコードでは、次のように書きます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

foreach (KeyValuePair<string, int> item in scores)
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

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

C#
田中:80
佐藤:95
鈴木:70

ただし、Dictionaryは基本的に「キーと値の対応関係」を扱うためのコレクションです。表示順が重要な処理では、順番についても注意が必要です。

1-4. まず押さえるべきKeyValuePairの考え方

Dictionaryforeachで扱うときに重要なのが、KeyValuePair<TKey, TValue>です。

KeyValuePairは、名前のとおり「キーと値のペア」を表します。

C#
KeyValuePair<string, int>

この場合、string型のキーと、int型の値を持つペアという意味です。

たとえば、次のDictionaryがあるとします。

C#
Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "りんご", 120 },
{ "バナナ", 90 },
{ "みかん", 100 }
};

このDictionaryforeachで回すと、各要素は次のようなイメージで取り出されます。

C#
Key = "りんご", Value = 120
Key = "バナナ", Value = 90
Key = "みかん", Value = 100

つまり、foreachの中で取り出しているitemは、キーと値を両方持った1つのデータです。

2. foreachでDictionaryのKeyとValueを取得する方法

2-1. KeyValuePair<TKey, TValue>を使ってKeyとValueを取得する

Dictionaryのキーと値を明示的に取得したい場合は、KeyValuePair<TKey, TValue>を使って書けます。

C#
Dictionary<string, int> ages = new Dictionary<string, int>
{
{ "田中", 25 },
{ "佐藤", 30 },
{ "鈴木", 28 }
};

foreach (KeyValuePair<string, int> item in ages)
{
string name = item.Key;
int age = item.Value;

Console.WriteLine($"{name}さんは{age}歳です。");
}

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

C#
田中さんは25歳です。
佐藤さんは30歳です。
鈴木さんは28歳です。

KeyValuePair<string, int>と書くことで、キーがstring型、値がint型であることが明確になります。

2-2. item.Keyでキーを取得する

foreachで取り出した要素からキーを取得するには、item.Keyを使います。

C#
foreach (var item in ages)
{
Console.WriteLine(item.Key);
}

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

C#
田中
佐藤
鈴木

キーだけを表示したい場合や、キーを条件判定に使いたい場合に便利です。

C#
foreach (var item in ages)
{
if (item.Key == "佐藤")
{
Console.WriteLine("佐藤さんのデータが見つかりました。");
}
}

2-3. item.Valueで値を取得する

値を取得したい場合は、item.Valueを使います。

C#
foreach (var item in ages)
{
Console.WriteLine(item.Value);
}

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

C#
25
30
28

値を使って条件分岐することもできます。

C#
foreach (var item in ages)
{
if (item.Value >= 30)
{
Console.WriteLine($"{item.Key}さんは30歳以上です。");
}
}

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

C#
佐藤さんは30歳以上です。

2-4. varを使って簡潔に書く方法

Dictionaryforeachでは、varを使って簡潔に書くことがよくあります。

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

これは次のコードと同じ意味です。

C#
foreach (KeyValuePair<string, int> item in ages)
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

varを使うと型名を省略できるため、コードが読みやすくなります。

特にDictionary<string, List<int>>のように型が長くなる場合は、varを使うと便利です。

C#
Dictionary<string, List<int>> testScores = new Dictionary<string, List<int>>
{
{ "田中", new List<int> { 80, 90, 85 } },
{ "佐藤", new List<int> { 95, 88, 92 } }
};

foreach (var item in testScores)
{
Console.WriteLine(item.Key);

foreach (var score in item.Value)
{
Console.WriteLine(score);
}
}

2-5. 文字列・数値など型別のサンプルコード

Dictionaryでは、キーと値にさまざまな型を指定できます。

キーがstring、値がstringの例です。

C#
Dictionary<string, string> capitals = new Dictionary<string, string>
{
{ "日本", "東京" },
{ "アメリカ", "ワシントンD.C." },
{ "フランス", "パリ" }
};

foreach (var item in capitals)
{
Console.WriteLine($"{item.Key}の首都は{item.Value}です。");
}

キーがint、値がstringの例です。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "田中" },
{ 2, "佐藤" },
{ 3, "鈴木" }
};

foreach (var item in users)
{
Console.WriteLine($"ID:{item.Key}, 名前:{item.Value}");
}

キーがstring、値がdoubleの例です。

C#
Dictionary<string, double> weights = new Dictionary<string, double>
{
{ "りんご", 1.2 },
{ "バナナ", 0.8 },
{ "みかん", 0.6 }
};

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

このように、foreachの書き方は型が変わっても基本的には同じです。

3. DictionaryのKeyだけをforeachでループする方法

3-1. Keysプロパティを使ったループ処理

Dictionaryのキーだけをループしたい場合は、Keysプロパティを使います。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

foreach (string key in scores.Keys)
{
Console.WriteLine(key);
}

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

C#
田中
佐藤
鈴木

Keysは、Dictionaryに登録されているキーの一覧を表します。

varを使って次のように書くこともできます。

C#
foreach (var key in scores.Keys)
{
Console.WriteLine(key);
}

3-2. Keyを使ってValueを参照する方法

Keysでキーだけをループしながら、必要に応じて値を取得することもできます。

C#
foreach (var key in scores.Keys)
{
int value = scores[key];
Console.WriteLine($"{key}:{value}");
}

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

C#
田中:80
佐藤:95
鈴木:70

ただし、キーと値の両方を使う場合は、最初からDictionary本体をforeachで回すほうが自然です。

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

Keysを使ってからdictionary[key]で値を取得する方法は、キー中心の処理をしたい場合に向いています。

3-3. Keyだけ必要なケースの具体例

キーだけを使うケースには、次のようなものがあります。

たとえば、登録されているユーザーIDだけを表示したい場合です。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 101, "田中" },
{ 102, "佐藤" },
{ 103, "鈴木" }
};

foreach (var userId in users.Keys)
{
Console.WriteLine($"ユーザーID:{userId}");
}

また、キーを使って別の処理を呼び出す場合にも使えます。

C#
foreach (var userId in users.Keys)
{
Console.WriteLine($"ユーザーID {userId} の処理を開始します。");
}

値そのものを使わない処理であれば、Keysを使うと意図がわかりやすくなります。

3-4. Keysでループするときの注意点

Keysでループしている間も、元のDictionaryを追加・削除するとエラーになることがあります。

C#
foreach (var key in scores.Keys)
{
scores.Remove(key);
}

このように、foreachで回している最中にDictionaryの要素数を変える処理は避けるべきです。

削除したい場合は、先に削除対象のキーをリスト化してから処理します。

C#
var removeKeys = scores.Keys.ToList();

foreach (var key in removeKeys)
{
scores.Remove(key);
}

ToList()を使うことで、Dictionaryのキー一覧を別のリストとして作成できます。そのリストをループしているため、元のDictionaryを変更しても安全に処理できます。

4. DictionaryのValueだけをforeachでループする方法

4-1. Valuesプロパティを使ったループ処理

Dictionaryの値だけをループしたい場合は、Valuesプロパティを使います。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

foreach (int value in scores.Values)
{
Console.WriteLine(value);
}

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

C#
80
95
70

Valuesは、Dictionaryに登録されている値の一覧を表します。

varを使って次のようにも書けます。

C#
foreach (var value in scores.Values)
{
Console.WriteLine(value);
}

4-2. Valueのみを一覧表示するサンプルコード

値だけを一覧表示したい場合は、次のように書きます。

C#
Dictionary<string, string> fruits = new Dictionary<string, string>
{
{ "apple", "りんご" },
{ "banana", "バナナ" },
{ "orange", "みかん" }
};

foreach (var value in fruits.Values)
{
Console.WriteLine(value);
}

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

C#
りんご
バナナ
みかん

キーが不要で、値だけ表示・集計・チェックしたい場合はValuesを使うとシンプルです。

4-3. Valueだけ必要なケースの具体例

値だけを使うケースとして、合計点を計算する例があります。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

int total = 0;

foreach (var score in scores.Values)
{
total += score;
}

Console.WriteLine($"合計点:{total}");

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

C#
合計点:245

平均点を求める場合は、次のように書けます。

C#
int total = 0;

foreach (var score in scores.Values)
{
total += score;
}

double average = (double)total / scores.Count;

Console.WriteLine($"平均点:{average}");

このように、値だけを使って集計したい場合はValuesが便利です。

4-4. Valuesでループするときの注意点

Valuesでループしている場合、キーは取得できません。

C#
foreach (var value in scores.Values)
{
Console.WriteLine(value);
}

この書き方では値だけしか取り出せないため、「どのキーの値なのか」を知ることはできません。

キーと値の両方が必要な場合は、ValuesではなくDictionary本体をループします。

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

また、Valuesでループしている途中にDictionaryへ要素を追加・削除することも避けましょう。

5. foreach中にDictionaryを変更するときの注意点

5-1. foreach中に要素を追加・削除するとエラーになる理由

foreachDictionaryをループしている最中に、要素を追加したり削除したりすると、InvalidOperationExceptionが発生することがあります。

これは、foreachがコレクションの状態を前提にして順番に要素を取り出しているためです。

ループ中に要素数が変わると、列挙処理の整合性が崩れるため、エラーになります。

次のような処理は避けるべきです。

C#
foreach (var item in dictionary)
{
dictionary.Add("newKey", 100);
}

また、削除も同様です。

C#
foreach (var item in dictionary)
{
dictionary.Remove(item.Key);
}

5-2. InvalidOperationExceptionが発生する例

次のコードは、foreach中にDictionaryから要素を削除しているため、実行時にエラーになる可能性があります。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

foreach (var item in scores)
{
if (item.Value < 80)
{
scores.Remove(item.Key);
}
}

このコードでは、点数が80未満の要素を削除しようとしています。

しかし、foreachscoresを回している最中にscores.Remove()を実行しているため、コレクションが変更されてしまいます。

その結果、次のような例外が発生します。

C#
System.InvalidOperationException

5-3. 削除したい場合はToListや別リストを使う

foreach中に削除したい場合は、削除対象を一度別のリストに退避してから削除します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

var removeKeys = new List<string>();

foreach (var item in scores)
{
if (item.Value < 80)
{
removeKeys.Add(item.Key);
}
}

foreach (var key in removeKeys)
{
scores.Remove(key);
}

また、LINQのToList()を使って、ループ対象をコピーしてから処理する方法もあります。

C#
foreach (var item in scores.ToList())
{
if (item.Value < 80)
{
scores.Remove(item.Key);
}
}

この場合、scores.ToList()で作成された別のリストをループしているため、元のscoresを削除しても安全です。

5-4. 値だけを更新する場合の考え方

Dictionaryの値を更新したい場合は、キーを使って代入します。

C#
scores["田中"] = 85;

ただし、foreachで取り出したitem.Valueに直接代入することはできません。

次のようなコードはコンパイルエラーになります。

C#
foreach (var item in scores)
{
item.Value = item.Value + 10;
}

KeyValuePairValueは読み取り専用のため、直接変更できません。

値を更新したい場合は、キーを使ってDictionary本体に代入します。

C#
foreach (var key in scores.Keys.ToList())
{
scores[key] = scores[key] + 10;
}

このように、キーの一覧をコピーしてから値を更新すると安全です。

5-5. 安全にDictionaryを変更するサンプルコード

点数が80未満のデータを削除し、残ったデータの点数を10点加算する例です。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

// 削除対象のキーを先に取得
var removeKeys = scores
.Where(item => item.Value < 80)
.Select(item => item.Key)
.ToList();

// 削除
foreach (var key in removeKeys)
{
scores.Remove(key);
}

// 値を更新
foreach (var key in scores.Keys.ToList())
{
scores[key] += 10;
}

// 結果表示
foreach (var item in scores)
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

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

C#
田中:90
佐藤:105

このように、追加・削除・更新を行う場合は、ループ対象をコピーする、削除対象を別リストにするなどの工夫が必要です。

6. Dictionaryをforeachで使う実践パターン

6-1. 条件に一致するKeyとValueだけを処理する

foreachの中でif文を使うと、条件に一致する要素だけを処理できます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 },
{ "高橋", 88 }
};

foreach (var item in scores)
{
if (item.Value >= 80)
{
Console.WriteLine($"{item.Key}:合格");
}
}

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

C#
田中:合格
佐藤:合格
高橋:合格

このように、item.Valueを条件に使うことで、値に応じた処理ができます。

キーで条件判定することもできます。

C#
foreach (var item in scores)
{
if (item.Key.StartsWith("佐"))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}
}

6-2. Dictionaryの中身をコンソールに出力する

Dictionaryの内容を確認したい場合は、foreachで出力するのが簡単です。

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

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

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

C#
Theme = Dark
Language = Japanese
FontSize = 14

デバッグ時にも、Dictionaryの中身を一覧表示する処理はよく使われます。

6-3. Dictionaryの値を集計する

Dictionaryの値が数値の場合、foreachで合計や最大値を求められます。

C#
Dictionary<string, int> sales = new Dictionary<string, int>
{
{ "1月", 100000 },
{ "2月", 150000 },
{ "3月", 120000 }
};

int total = 0;

foreach (var item in sales)
{
total += item.Value;
}

Console.WriteLine($"売上合計:{total}");

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

C#
売上合計:370000

最大値を求める例です。

C#
int max = 0;
string maxMonth = "";

foreach (var item in sales)
{
if (item.Value > max)
{
max = item.Value;
maxMonth = item.Key;
}
}

Console.WriteLine($"最大売上:{maxMonth} {max}");

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

C#
最大売上:2月 150000

6-4. Dictionaryから別のDictionaryやListを作成する

foreachを使って、既存のDictionaryから別のコレクションを作成できます。

たとえば、80点以上のデータだけを新しいDictionaryに追加する例です。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 },
{ "高橋", 88 }
};

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

foreach (var item in scores)
{
if (item.Value >= 80)
{
passedScores.Add(item.Key, item.Value);
}
}

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

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

C#
田中:80
佐藤:95
高橋:88

Listに変換することもできます。

C#
List<string> names = new List<string>();

foreach (var item in scores)
{
names.Add(item.Key);
}

この例では、DictionaryのキーだけをList<string>に追加しています。

6-5. LINQと組み合わせてループ前に絞り込む

DictionaryはLINQと組み合わせて使うこともできます。

たとえば、80点以上のデータだけを先に絞り込んでからforeachで処理する場合は、次のように書けます。

C#
foreach (var item in scores.Where(x => x.Value >= 80))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

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

C#
田中:80
佐藤:95
高橋:88

キーで並び替えてからループすることもできます。

C#
foreach (var item in scores.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

値で降順に並び替える場合は次のように書きます。

C#
foreach (var item in scores.OrderByDescending(x => x.Value))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

LINQを使うと、ループ前に絞り込みや並び替えができるため、コードの意図がわかりやすくなります。

7. foreach以外でDictionaryをループする方法

7-1. for文でDictionaryを処理できるか

Dictionaryは配列やListのようにインデックスで要素を管理するコレクションではありません。

そのため、次のような書き方はできません。

C#
for (int i = 0; i < dictionary.Count; i++)
{
Console.WriteLine(dictionary[i]);
}

Dictionary[]にはインデックスではなくキーを指定します。

C#
scores["田中"]

つまり、Dictionaryは「0番目」「1番目」のように取り出すのではなく、「このキーに対応する値」を取り出すためのコレクションです。

7-2. ElementAtを使う方法と注意点

LINQのElementAtを使うと、指定した位置の要素を取得することはできます。

C#
var item = scores.ElementAt(0);

Console.WriteLine($"{item.Key}:{item.Value}");

ただし、Dictionaryは順番を前提に使うコレクションではありません。

そのため、「0番目の要素を取りたい」という処理は、基本的にはDictionaryに向いていません。

また、ElementAtをループの中で何度も使うと、パフォーマンス面で不利になることがあります。

C#
for (int i = 0; i < scores.Count; i++)
{
var item = scores.ElementAt(i);
Console.WriteLine($"{item.Key}:{item.Value}");
}

このような処理をするくらいなら、通常はforeachを使うほうが自然です。

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

7-3. LINQのSelectやWhereを使う方法

DictionaryはLINQのWhereSelectと組み合わせて処理できます。

条件に一致する要素だけを取得する例です。

C#
var highScores = scores.Where(item => item.Value >= 80);

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

キーだけを取り出す場合はSelectを使えます。

C#
var names = scores.Select(item => item.Key);

foreach (var name in names)
{
Console.WriteLine(name);
}

値だけを加工して取り出すこともできます。

C#
var bonusScores = scores.Select(item => item.Value + 10);

foreach (var score in bonusScores)
{
Console.WriteLine(score);
}

LINQは便利ですが、処理が複雑になりすぎると読みにくくなります。単純に全件処理するだけなら、foreachのほうがわかりやすい場合も多いです。

7-4. foreachとfor文・LINQの使い分け

Dictionaryをループ処理する場合、基本はforeachを使うのがおすすめです。

C#
foreach (var item in dictionary)
{
// KeyとValueを処理する
}

条件で絞り込みたい場合は、foreach内でifを使うか、LINQのWhereを使います。

C#
foreach (var item in dictionary.Where(x => x.Value > 0))
{
// 条件に一致する要素だけ処理する
}

値を変換して新しいコレクションを作る場合は、LINQのSelectが便利です。

C#
var list = dictionary.Select(x => x.Key).ToList();

一方、for文はDictionaryとは相性がよくありません。インデックスで順番に処理したい場合は、Listなどの別のコレクションを検討しましょう。

8. Dictionaryのforeachでよくあるエラーと解決方法

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

KeyNotFoundExceptionは、存在しないキーを指定して値を取得しようとしたときに発生します。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 }
};

Console.WriteLine(scores["鈴木"]);

この例では、"鈴木"というキーが存在しないためエラーになります。

foreachの中でも、別のキーを使って値を取得する場合には注意が必要です。

C#
foreach (var item in scores)
{
Console.WriteLine(scores["鈴木"]);
}

このようなコードは、キーが存在しない場合に例外が発生します。

8-2. コレクション変更によるInvalidOperationException

foreach中にDictionaryへ要素を追加・削除すると、InvalidOperationExceptionが発生することがあります。

C#
foreach (var item in scores)
{
scores.Remove(item.Key);
}

このような処理は、ループ中にコレクションの状態を変更しているため危険です。

削除する場合は、削除対象を先にリスト化します。

C#
var removeKeys = scores.Keys.ToList();

foreach (var key in removeKeys)
{
scores.Remove(key);
}

追加する場合も、ループが終わってから追加するか、追加対象を別のリストにまとめておきましょう。

8-3. nullのValueを扱うときの注意点

Dictionaryの値が参照型の場合、Valuenullになることがあります。

C#
Dictionary<string, string?> users = new Dictionary<string, string?>
{
{ "user1", "田中" },
{ "user2", null },
{ "user3", "鈴木" }
};

この状態で、Valueに対してそのままメソッドを呼び出すとエラーになる可能性があります。

C#
foreach (var item in users)
{
Console.WriteLine(item.Value.Length);
}

item.Valuenullの場合、NullReferenceExceptionが発生します。

安全に処理するには、nullチェックを行います。

C#
foreach (var item in users)
{
if (item.Value != null)
{
Console.WriteLine($"{item.Key}:{item.Value.Length}文字");
}
else
{
Console.WriteLine($"{item.Key}:名前が登録されていません");
}
}

また、null条件演算子を使うこともできます。

C#
foreach (var item in users)
{
Console.WriteLine(item.Value?.Length);
}

8-4. 型が合わないときのコンパイルエラー

Dictionaryのキーや値の型と、foreachで受け取る型が合わない場合はコンパイルエラーになります。

たとえば、次のDictionaryがあるとします。

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

この場合、要素の型はKeyValuePair<string, int>です。

そのため、次のような書き方はできません。

C#
foreach (string item in scores)
{
Console.WriteLine(item);
}

scoresを直接ループすると、取り出されるのはstringではなくKeyValuePair<string, int>です。

正しくは次のように書きます。

C#
foreach (KeyValuePair<string, int> item in scores)
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

または、varを使います。

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

キーだけをループしたい場合はKeysを使います。

C#
foreach (string key in scores.Keys)
{
Console.WriteLine(key);
}

値だけをループしたい場合はValuesを使います。

C#
foreach (int value in scores.Values)
{
Console.WriteLine(value);
}

8-5. TryGetValueを使った安全な取得方法

存在しないキーを指定してエラーになるのを防ぐには、TryGetValueを使うのが安全です。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 }
};

if (scores.TryGetValue("鈴木", out int score))
{
Console.WriteLine($"鈴木さんの点数:{score}");
}
else
{
Console.WriteLine("鈴木さんの点数は登録されていません。");
}

TryGetValueは、キーが存在する場合はtrueを返し、値をout引数に入れます。

キーが存在しない場合はfalseを返すため、例外を発生させずに処理できます。

foreachと組み合わせる場合も便利です。

C#
Dictionary<string, string> userNames = new Dictionary<string, string>
{
{ "001", "田中" },
{ "002", "佐藤" }
};

List<string> targetIds = new List<string> { "001", "003" };

foreach (var id in targetIds)
{
if (userNames.TryGetValue(id, out string name))
{
Console.WriteLine($"{id}:{name}");
}
else
{
Console.WriteLine($"{id}:未登録");
}
}

このように、キーが存在するかわからない場合は、dictionary[key]よりもTryGetValueを使うほうが安全です。

9. Dictionaryをforeachで扱うときの順番とパフォーマンス

9-1. Dictionaryのループ順は保証されるのか

Dictionaryforeachでループしたときの順番は、基本的に処理の前提にしないほうが安全です。

次のように登録した場合でも、常にこの順番で処理されることを前提にするのは避けましょう。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

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

Dictionaryは、順番を管理するためのコレクションではなく、キーから値を効率よく取得するためのコレクションです。

そのため、「登録順に処理したい」「キー順に処理したい」「値の大きい順に処理したい」といった場合は、別の方法を使いましょう。

9-2. 順番が必要な場合の対処法

順番が必要な場合は、OrderByOrderByDescendingを使って並び替えてからループします。

キー順に並び替える例です。

C#
foreach (var item in scores.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

値の昇順に並び替える例です。

C#
foreach (var item in scores.OrderBy(x => x.Value))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

値の降順に並び替える例です。

C#
foreach (var item in scores.OrderByDescending(x => x.Value))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

順番を明確にしたい場合は、Dictionaryそのもののループ順に頼らず、明示的に並び替えるのがおすすめです。

9-3. SortedDictionaryやOrderByを使う方法

キーの順番を常に保ちたい場合は、SortedDictionary<TKey, TValue>を使う方法があります。

C#
SortedDictionary<string, int> scores = new SortedDictionary<string, int>
{
{ "田中", 80 },
{ "佐藤", 95 },
{ "鈴木", 70 }
};

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

SortedDictionaryは、キーの順番で管理されるコレクションです。

一方、通常のDictionaryを一時的に並び替えたいだけなら、OrderByを使うほうが手軽です。

C#
foreach (var item in scores.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

常に並び順が必要ならSortedDictionary、必要なときだけ並び替えたいならOrderBy、という使い分けができます。

9-4. 大量データをforeachする場合のパフォーマンス注意点

大量のデータをforeachで処理する場合、不要な処理をループ内に書かないことが重要です。

たとえば、ループのたびに重い処理を行うと、全体の処理時間が大きくなります。

C#
foreach (var item in dictionary)
{
// 重い処理を毎回実行すると遅くなる可能性がある
}

また、OrderByToListは便利ですが、データを並び替えたりコピーしたりするため、件数が多い場合はコストがかかります。

C#
foreach (var item in dictionary.OrderBy(x => x.Key))
{
Console.WriteLine(item.Key);
}

並び順が不要なら、単純なforeachのほうが効率的です。

C#
foreach (var item in dictionary)
{
Console.WriteLine(item.Key);
}

大量データを扱う場合は、次の点を意識しましょう。

C#
// 必要がなければToListしない
// 必要がなければOrderByしない
// ループ内で重い処理を繰り返さない
// Keyだけ、Valueだけでよい場合はKeysやValuesを使う

処理の意図に合わせて、シンプルなループにすることが大切です。

10. C# Dictionaryのforeachに関するよくある質問

10-1. foreachでKeyとValueを同時に取得できますか

はい、取得できます。

Dictionaryをそのままforeachでループすると、各要素はKeyValuePair<TKey, TValue>として取り出されます。

C#
foreach (var item in dictionary)
{
Console.WriteLine(item.Key);
Console.WriteLine(item.Value);
}

item.Keyでキー、item.Valueで値を取得できます。

明示的に書く場合は次のようになります。

C#
foreach (KeyValuePair<string, int> item in scores)
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

C# 7以降では、分解を使って次のように書くこともできます。

C#
foreach (var (key, value) in scores)
{
Console.WriteLine($"{key}:{value}");
}

ただし、プロジェクトのC#バージョンによっては使えない場合があるため、基本としてはitem.Keyitem.Valueを覚えておくと安心です。

10-2. foreach中にDictionaryの要素を削除できますか

foreachDictionaryを回している最中に、そのDictionaryから直接要素を削除するのは避けましょう。

次のようなコードはエラーになる可能性があります。

C#
foreach (var item in scores)
{
scores.Remove(item.Key);
}

削除したい場合は、削除対象のキーを先にリスト化してから削除します。

C#
var removeKeys = scores
.Where(x => x.Value < 80)
.Select(x => x.Key)
.ToList();

foreach (var key in removeKeys)
{
scores.Remove(key);
}

または、scores.ToList()でコピーを作ってからループする方法もあります。

C#
foreach (var item in scores.ToList())
{
if (item.Value < 80)
{
scores.Remove(item.Key);
}
}

10-3. DictionaryのValueだけを変更できますか

できます。ただし、foreachで取得したitem.Valueに直接代入することはできません。

次のコードは正しくありません。

C#
foreach (var item in scores)
{
item.Value = item.Value + 10;
}

値を変更する場合は、キーを使ってDictionary本体に代入します。

C#
foreach (var key in scores.Keys.ToList())
{
scores[key] = scores[key] + 10;
}

このように、キーの一覧をコピーしてから値を更新すると安全です。

10-4. Dictionaryの順番通りにループできますか

Dictionaryのループ順を処理の前提にするのは避けたほうが安全です。

順番が必要な場合は、OrderByなどで明示的に並び替えます。

C#
foreach (var item in scores.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

値の大きい順に処理したい場合は、OrderByDescendingを使います。

C#
foreach (var item in scores.OrderByDescending(x => x.Value))
{
Console.WriteLine($"{item.Key}:{item.Value}");
}

キー順を常に保ちたい場合は、SortedDictionaryも選択肢になります。

C#
SortedDictionary<string, int> sortedScores = new SortedDictionary<string, int>();

10-5. KeyValuePairを使わずに書く方法はありますか

KeyValuePairを明示的に書かずに、varを使って簡潔に書けます。

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

この場合でも、itemの実際の型はKeyValuePair<TKey, TValue>です。

また、C#の分解を使える環境では、次のように書くこともできます。

C#
foreach (var (key, value) in scores)
{
Console.WriteLine($"{key}:{value}");
}

ただし、もっとも基本的でわかりやすいのは、var itemを使ってitem.Keyitem.Valueを参照する書き方です。

まとめ

C#のDictionaryは、foreachを使うことで簡単にループ処理できます。

キーと値の両方を使いたい場合は、Dictionary本体をそのままforeachで回します。

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

キーだけを使いたい場合はKeysプロパティを使います。

C#
foreach (var key in dictionary.Keys)
{
Console.WriteLine(key);
}

値だけを使いたい場合はValuesプロパティを使います。

C#
foreach (var value in dictionary.Values)
{
Console.WriteLine(value);
}

Dictionaryforeachで扱うときは、次の点に注意しましょう。

C#
// KeyとValueは item.Key / item.Value で取得する
// Keyだけなら Keys、Valueだけなら Values を使う
// foreach中に追加・削除するとエラーになることがある
// 削除したい場合は ToList や別リストを使う
// 存在しないキーの取得には TryGetValue を使う
// 順番が必要なら OrderBy や SortedDictionary を使う

Dictionaryforeachは、C#で実務的にもよく使う基本処理です。

KeyValuePairの考え方、KeysValuesの使い分け、ループ中の変更に関する注意点を押さえておけば、安全で読みやすいコードを書けるようになります。