悩んでる人C#のDictionaryをforeachで回したいけど、書き方がよくわからない



KeyValuePairって何?varで書けるの?ループ中に変更したらエラーになった!
C#でDictionaryを使っていると、foreachでどう書けばいいか迷う場面は多いですよね。
キーだけ欲しいとき、値だけ欲しいとき、ループ中に変更したいときで、書き方が変わるので、覚えづらいと思います。
本記事では、C#のDictionary × foreachを基本から応用まで、サンプルコードつきで丁寧に解説します!
実務でよく使うパターンも紹介するので、ぜひ最後まで読んでください!
- Dictionaryをforeachでループする基本的な書き方
- キーのみ・値のみを取得する方法(Keys / Values)
- タプル構文でスッキリ書く方法(C# 7以降)
- ループ中にDictionaryを変更する方法(InvalidOperationException対処)
- LINQを使ってソート(OrderBy)しながらforeachする方法
- for文との違いとパフォーマンスの考え方


- システムエンジニア10年目
- 会社員3年⇒フリーランス7年目
- 保険系 / 物流系 / 鉄道系 / 小売系などのシステム開発に従事
- プログラミング講師
Dictionary と foreach の基本を理解しよう


C#の Dictionary は、「キー(Key)」と「値(Value)」をペアで管理するコレクションです。
配列やListがインデックス番号で管理するのに対し、Dictionaryはキーを使って値を取り出します。





Dictionaryにforeachって使えるの?インデックスがないのに…
使えます!Dictionaryはforeachで回せるように設計されています。ループ変数は「KeyValuePair」という型になりますよ。
DictionaryはListと同様に IEnumerable インターフェースを実装しているため、foreachでループ処理が可能です。
- Dictionaryをforeachでループすると、各要素はKeyValuePair<TKey, TValue> 型として取り出されます。
- KeyプロパティにキーAlue、Valueプロパティに値が格納されています。
基本の書き方:キーと値を同時に取得する


まずは、一番基本的な書き方から見ていきましょう。
Dictionaryをforeachでループするとき、各要素は KeyValuePair として取り出されます。


using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 社員名とスコアのDictionary
var scores = new Dictionary<string, int>
{
{ "田中", 85 },
{ "鈴木", 92 },
{ "佐藤", 78 }
};
// ① var を使う書き方(推奨)
foreach (var pair in scores)
{
Console.WriteLine($"{pair.Key}: {pair.Value}点");
}
// ② KeyValuePair を明示する書き方
foreach (KeyValuePair<string, int> pair in scores)
{
Console.WriteLine($"{pair.Key}: {pair.Value}点");
}
}
}田中: 85点
鈴木: 92点
佐藤: 78点
- var を使った書き方が実務では主流です。
- 型は自動で
KeyValuePair<string, int>と推論されるので、記述がシンプルになります。 - キーは
pair.Key、値はpair.Valueでアクセスできます。
①と②は結果は同じです!実務では短く書ける「var」を使う方が断然多いですよ。
キーだけ・値だけを取得する方法


キーだけ・値だけを取り出したい場合は、Keys プロパティ / Values プロパティ を使います。
var scores = new Dictionary<string, int>
{
{ "田中", 85 }, { "鈴木", 92 }, { "佐藤", 78 }
};
// ▼ キーだけ取得
foreach (var key in scores.Keys)
{
Console.WriteLine(key); // 田中, 鈴木, 佐藤
}
// ▼ 値だけ取得
foreach (var val in scores.Values)
{
Console.WriteLine(val); // 85, 92, 78
}
// ⚠️ これはNG(二重検索になり非効率)
foreach (var key in scores.Keys)
{
Console.WriteLine(scores[key]); // 毎回検索が発生する
}
// ✅ キーと値の両方が必要なら直接ループする
foreach (var pair in scores)
{
Console.WriteLine($"{pair.Key}: {pair.Value}");
}キーだけでよい場合は dic.Keys でOKです。
しかし、 foreach (var key in dic.Keys) { var val = dic[key]; } のようにループ内で値を再取得すると、Dictionaryへの二重検索が発生して非効率になります。
キーと値の両方が必要なら、最初から foreach (var pair in dic) を使いましょう。
タプル構文でスッキリ書く(C# 7以降)


C# 7(.NET Core 2.0以降)では、タプル構文を使ってforeachをさらにスッキリ書けるようになりました。
pair.Key / pair.Value の代わりに、自分でわかりやすい変数名を直接指定できます。
var scores = new Dictionary<string, int>
{
{ "田中", 85 }, { "鈴木", 92 }, { "佐藤", 78 }
};
// ✅ タプル構文(C# 7 / .NET Core 2.0以降)
foreach (var (name, score) in scores)
{
Console.WriteLine($"{name}: {score}点");
}田中: 85点
鈴木: 92点
佐藤: 78点
キーだけ欲しいときはアンダースコアで値を破棄します。
var scores = new Dictionary<string, int>
{
{ "田中", 85 }, { "鈴木", 92 }, { "佐藤", 78 }
};
// キーだけ欲しいときはアンダースコアで値を破棄
foreach (var (name, _) in scores)
{
Console.WriteLine(name);
}田中
鈴木
佐藤
値だけ欲しいときはキーを破棄します。
var scores = new Dictionary<string, int>
{
{ "田中", 85 }, { "鈴木", 92 }, { "佐藤", 78 }
};
// 値だけ欲しいときはキーを破棄
foreach (var (_, score) in scores)
{
Console.WriteLine(score);
}85
92
78
アンダースコア「_」を使うと「この変数は使わない」という意図を明示できます。不要な変数名をつけなくていいので、コードがより読みやすくなりますよ!
- .NET Framework環境では標準でタプル構文が使えません。
- その場合は、拡張メソッドで
Deconstructを追加することで対応できます。 - .NET Core 2.0以降 / .NET 5以降であれば、追加作業なしで使えます。
ループ中にDictionaryを変更する方法


foreachのループ中にDictionaryを変更しようとすると、InvalidOperationException が発生します。
これは、foreachが内部でEnumerator(列挙子)を使っており、途中でコレクションが変わると正しく動作できなくなるためです。
var users = new Dictionary<int, string>
{
{ 1, "山田" }, { 2, "田中" }, { 3, "鈴木" }
};
// ❌ NG:ループ中に要素を追加 → 例外発生!
foreach (var item in users)
{
users.Add(4, "佐藤"); // 💥 InvalidOperationException
}


なんで直接変更するとエラーになるの?
foreachはDictionary内部の Enumerator(列挙子)を使って要素を順番に取り出しています。
ループ中にAdd・Remove・値の代入などコレクション自体が変化すると、列挙子が無効になるのでエラーとなります。



じゃあ、どうすればいいんだろう?
- 値を更新したい:
users.Keys.ToList()でキーをコピーしてからループ - 要素を削除したい:
users.Keys.ToList()でコピー後Remove()を呼ぶ - 要素を追加したい:ループ後に追加するか、別リストに溜めてからAddする
var users = new Dictionary<int, string>
{
{ 1, "山田" }, { 2, "田中" }, { 3, "鈴木" }
};
// ✅ OK:値を更新したい → Keys.ToList() でループ
foreach (var key in users.Keys.ToList())
{
users[key] = "更新済み"; // ✅ OK:値の変更はエラーなし
}
// ✅ OK:要素を削除したい → ToList() でコピーしてからRemove
foreach (var key in users.Keys.ToList())
{
if (key == 2)
{
users.Remove(key); // ✅ ToList()後なのでOK
}
}LINQを使ってソートしながらforeachする


Dictionaryは、要素の順序を保証しません。
foreachで取り出す順番は不定です。
ソートが必要なときは、LINQの OrderBy / OrderByDescending と組み合わせて使います。


using System.Linq; // LINQ を使うために必要
var scores = new Dictionary<string, int>
{
{ "田中", 85 }, { "鈴木", 92 }, { "佐藤", 78 },
{ "山田", 71 }, { "高橋", 65 }
};
// ▼ 値(スコア)の昇順でループ
foreach (var pair in scores.OrderBy(x => x.Value))
{
Console.WriteLine($"{pair.Key}: {pair.Value}点");
}
// ▼ 値(スコア)の降順でループ(高い順)
// 実務でランキング表示によく使われる!
var rank = 1;
foreach (var pair in scores.OrderByDescending(x => x.Value))
{
Console.WriteLine($"{rank++}位:{pair.Key}({pair.Value}点)");
}1位:鈴木(92点)
2位:田中(85点)
3位:佐藤(78点)
4位:山田(71点)
5位:高橋(65点)
キー(名前)のアルファベット順でループする場合は、次のように書きます。
// ▼ キー(名前)のアルファベット順でループ
foreach (var pair in scores.OrderBy(x => x.Key))
{
Console.WriteLine($"{pair.Key}: {pair.Value}");
}「試してみたら登録した順番通りに出てきた」という経験をすることがあります。
しかしこれは仕様ではなく偶然です。
順番を保証したい場合は、必ずLINQのOrderByを使いましょう。
for文との違いとパフォーマンス


Dictionaryを for 文でループすることは技術的には可能ですが、パフォーマンス上の問題があります。
ElementAt(i) を使った for ループは計算量が O(n²) になり、要素が多いほど遅くなります。


using System.Linq;
var dic = new Dictionary<string, int>
{
{ "田中", 85 }, { "鈴木", 92 }, { "佐藤", 78 }
};
// ✅ 推奨:foreach でループ O(n)
foreach (var pair in dic)
{
Console.WriteLine($"{pair.Key}: {pair.Value}");
}
// ❌ 非推奨:for + ElementAt → O(n²) になる!
for (int i = 0; i < dic.Count; i++)
{
var pair = dic.ElementAt(i); // ← 毎回先頭から探索する
Console.WriteLine($"{pair.Key}: {pair.Value}");
}
// 💡 インデックス番号が必要なときはこちらを使う
var index = 0;
foreach (var pair in dic)
{
Console.WriteLine($"[{index}] {pair.Key}: {pair.Value}");
index++;
}Dictionaryは配列のような連続したメモリ配置ではありません。
ElementAt(i) は毎回先頭から i 番目の要素を探索するため、ループ全体で O(n²) の計算量になります。
要素数が増えると処理時間が指数的に増加するので、Dictionaryのループはforeachを使いましょう。
よくあるミスとその対策


C#のDictionary foreachでよくあるミスと対策をまとめます。
❌ ミス① ループ中にDictionaryを変更してしまう
InvalidOperationException: Collection was modified; enumeration operation may not execute.
Keys.ToList() でキーをコピーしてからループする
❌ ミス② 取り出し順に依存したコードを書いてしまう
「追加した順番に取り出せる」と思い込むのは危険です。
順番が必要な場合は OrderBy / OrderByDescending でソートしてからforeachする
❌ ミス③ Keys でループして dic[key] で値を取り出す
ループごとにDictionaryの再検索(ハッシュテーブルルックアップ)が発生します。
キーと値の両方が必要なら foreach (var pair in dic) を使う
| シチュエーション | 推奨する書き方 | 評価 |
|---|---|---|
| キーと値の両方が必要 | foreach (var pair in dic) | ◎ 最推奨 |
| キーだけ必要 | foreach (var key in dic.Keys) | ◎ |
| 値だけ必要 | foreach (var val in dic.Values) | ◎ |
| 変数名を明確にしたい | foreach (var (name, score) in dic) | ◎(C#7以降) |
| ソートしながらループ | foreach (var pair in dic.OrderBy(x => x.Value)) | ◎ |
| ループ中に要素を変更 | foreach (var key in dic.Keys.ToList()) | ◎ |
| for + ElementAt でループ | — | ❌ 非推奨(O(n²)) |
まとめ


- Dictionaryをforeachでループすると、各要素は KeyValuePair\ として取り出される
- 実務で最もよく使う書き方は
foreach (var pair in dic)(varによる型推論) - キーだけなら
dic.Keys、値だけならdic.Valuesでループできる - C# 7 / .NET Core 2.0以降なら
foreach (var (key, val) in dic)のタプル構文が使える - ループ中に変更するとエラーが出るため、
Keys.ToList()でコピーしてからループする - 取り出し順は保証されないのでソートが必要なときはLINQの
OrderByを組み合わせる - for + ElementAt でのループは O(n²) になるため非推奨。Dictionary は foreach が正解
Dictionary × foreach は、覚えるパターンがいくつかありますが、まずは「foreach (var pair in dic)」の基本形から使ってみてください。実務でコードを書くうちに自然と使い分けられるようになりますよ!
C#のDictionaryをforeachでループする方法を、基本から注意点まで解説しました。
次のステップとして、LINQをもっと活用したり、ConcurrentDictionary(スレッドセーフ版)についても調べてみると理解がさらに深まります!
