foreach 文(C#)
コレクションや配列の全要素を順に処理する『foreach』文の基本構文です。『C#』の foreach は IEnumerable インターフェースを実装した型であればどれにでも使え、インデックスを意識せずシンプルに要素を取り出すことができます。インデックスが必要な場合の代替手段や、for 文との使い分けも解説します。
構文
foreach 文の基本形と主要なパターンです。
// 基本的なforeach文です。
foreach (型 変数 in コレクション) {
// 各要素について繰り返し実行されます。
// 変数には現在の要素が自動的に代入されます。
}
// var を使って型推論させることもできます(推奨)。
foreach (var 変数 in コレクション) {
// 型は右辺のコレクションの要素型から自動推論されます。
}
// break でループを途中で抜けます。
foreach (var 変数 in コレクション) {
if (条件) {
break; // ループ全体を終了します。
}
}
// continue でその周だけスキップします。
foreach (var 変数 in コレクション) {
if (条件) {
continue; // この周の残り処理をスキップして次の周へ進みます。
}
}
foreach が使える型の条件
foreach は IEnumerable または IEnumerable<T> を実装している型に対して使えます。標準ライブラリの主なコレクションはすべてこのインターフェースを実装しています。
| 型 | foreach 使用可否 | 備考 |
|---|---|---|
| 配列(T[]) | 使用可 | IEnumerable<T> を実装しています。 |
| List<T> | 使用可 | 最もよく使うコレクションです。 |
| Dictionary<TKey, TValue> | 使用可 | KeyValuePair<TKey, TValue> として取り出されます。 |
| HashSet<T> | 使用可 | 順序は保証されません。 |
| IEnumerable<T> を実装した独自クラス | 使用可 | GetEnumerator() を実装すれば foreach に対応できます。 |
| int・string などのスカラー型 | 使用不可 | コンパイルエラーになります。 |
for と foreach の使い分け
| 状況 | 推奨 | 理由 |
|---|---|---|
| 全要素を順に処理するだけ | foreach | インデックスが不要でコードがシンプルになります。 |
| インデックスが必要 | for または Select with index | foreach 単体ではインデックスを取得できません。 |
| 逆順・ステップ変更 | for | カウンタを自由に操作できます。 |
| 要素の書き換えが必要 | for | foreach の変数はコピーのため元コレクションは変更されません。 |
| IEnumerable を直接処理(遅延評価) | foreach | LINQ クエリの結果など IList でない型にも対応できます。 |
サンプルコード
ForeachBasic.cs
配列・List に対する foreach と、break・continue の使い方を示すサンプルです。
using System;
using System.Collections.Generic;
class ForeachBasic {
static void Main() {
// --- 配列に対するforeachです ---
string[] items = { "item_a", "item_b", "item_c", "item_d", "item_e" };
Console.WriteLine("=== アイテムリスト ===");
foreach (string item in items) {
Console.WriteLine("アイテム: " + item);
}
// --- List<T> に対するforeachです ---
List<int> scores = new List<int> { 90, 85, 35, 15, 8 };
Console.WriteLine("\n=== スコア集計 ===");
int total = 0;
foreach (var score in scores) {
// var を使うと型を明示しなくても int と推論されます。
total += score;
Console.WriteLine("スコア: " + score);
}
Console.WriteLine("合計スコア: " + total);
// --- break: 条件を満たしたら探索を終了します ---
// インデックス不要の単純な検索は foreach + break が読みやすいです。
string[] sorted = { "item_d", "item_c", "item_b", "item_a", "item_e" };
string target = "item_b";
Console.WriteLine("\n=== " + target + " を探索 ===");
foreach (string name in sorted) {
Console.WriteLine(name + " を確認中...");
if (name == target) {
Console.WriteLine(target + " を発見!探索終了。");
break; // 対象を見つけたのでループを終了します。
}
}
// --- continue: 特定の要素をスキップします ---
// item_e を除く要素だけを表示します。
Console.WriteLine("\n=== item_e を除く要素 ===");
foreach (string name in items) {
if (name == "item_e") {
continue; // この周をスキップして次へ進みます。
}
Console.WriteLine(name);
}
}
}
コンパイルして実行すると次のようになります。
dotnet script ForeachBasic.cs === アイテムリスト === アイテム: item_a アイテム: item_b アイテム: item_c アイテム: item_d アイテム: item_e === スコア集計 === スコア: 90 スコア: 85 スコア: 35 スコア: 15 スコア: 8 合計スコア: 233 === item_b を探索 === item_d を確認中... item_c を確認中... item_b を確認中... item_b を発見!探索終了。 === item_e を除く要素 === item_a item_b item_c item_d
ForeachWithIndex.cs
foreach でインデックスが必要な場合の3つの代替手段と、Dictionary に対する foreach です。
using System;
using System.Collections.Generic;
using System.Linq;
class ForeachWithIndex {
static void Main() {
// foreach は標準ではインデックスを持ちません。
// インデックスが必要な場合の代替手段を3つ紹介します。
string[] phases = {
"Phase 1",
"Phase 2",
"Phase 3",
"Phase 4"
};
// --- 方法1: for文を使います(最もシンプルな方法)---
Console.WriteLine("=== 方法1: for文 ===");
for (int i = 0; i < phases.Length; i++) {
Console.WriteLine((i + 1) + ". " + phases[i]);
}
// --- 方法2: LINQ の Select で (要素, インデックス) のペアを生成します ---
// .NET 6 以降では Select((element, index) => ...) が使えます。
Console.WriteLine("\n=== 方法2: Select with index(LINQ) ===");
foreach (var item in phases.Select((phase, index) => new { phase, index })) {
// item.index でインデックスに、item.phase で要素にアクセスします。
Console.WriteLine((item.index + 1) + ". " + item.phase);
}
// --- 方法3: 手動カウンタ変数を用意します ---
Console.WriteLine("\n=== 方法3: 手動カウンタ ===");
int count = 0; // foreach の外でカウンタを宣言します。
foreach (var phase in phases) {
count++;
Console.WriteLine(count + ". " + phase);
}
// --- Dictionary に対するforeach ---
// KeyValuePair<TKey, TValue> として取り出されます。
Console.WriteLine("\n=== Dictionary の foreach ===");
var scoreDict = new Dictionary<string, int> {
{ "item_a", 90 },
{ "item_b", 85 },
{ "item_c", 120 },
{ "item_d", 35 }
};
foreach (var kvp in scoreDict) {
// kvp.Key でキーに、kvp.Value で値にアクセスします。
Console.WriteLine(kvp.Key + " のスコア: " + kvp.Value);
}
}
}
コンパイルして実行すると次のようになります。
dotnet script ForeachWithIndex.cs === 方法1: for文 === 1. Phase 1 2. Phase 2 3. Phase 3 4. Phase 4 === 方法2: Select with index(LINQ) === 1. Phase 1 2. Phase 2 3. Phase 3 4. Phase 4 === 方法3: 手動カウンタ === 1. Phase 1 2. Phase 2 3. Phase 3 4. Phase 4 === Dictionary の foreach === item_a のスコア: 90 item_b のスコア: 85 item_c のスコア: 120 item_d のスコア: 35
ForeachIEnumerable.cs
IEnumerable<T> を実装した独自コレクションで foreach を使うサンプルです。
using System;
using System.Collections;
using System.Collections.Generic;
// IEnumerable<T> を実装してforeachに対応した独自コレクションです。
// アイテム名を管理するリストです。
class ItemRoster : IEnumerable<string> {
private List<string> entries = new List<string>();
// アイテムを追加するメソッドです。
public void Add(string name) {
entries.Add(name);
}
// IEnumerable<T> の実装: GetEnumerator を提供するとforeachが使えます。
public IEnumerator<string> GetEnumerator() {
return entries.GetEnumerator();
}
// IEnumerable(非ジェネリック版)の実装も必要です。
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
class ForeachIEnumerable {
static void Main() {
// --- 独自コレクションをforeachで走査します ---
var roster = new ItemRoster();
roster.Add("item_a");
roster.Add("item_b");
roster.Add("item_c");
roster.Add("item_d");
roster.Add("item_e");
Console.WriteLine("=== ロスター一覧 ===");
foreach (string name in roster) {
// ItemRoster は IEnumerable<string> を実装しているのでforeachが使えます。
Console.WriteLine("登録済み: " + name);
}
// --- LINQ メソッドもそのまま使えます ---
// IEnumerable<T> を実装していれば Where / Select 等も利用可能です。
Console.WriteLine("\n=== 名前が6文字以上の要素 ===");
foreach (string name in roster) {
if (name.Length >= 6) {
Console.WriteLine(name + "(" + name.Length + "文字)");
}
}
}
}
コンパイルして実行すると次のようになります。
dotnet script ForeachIEnumerable.cs === ロスター一覧 === 登録済み: item_a 登録済み: item_b 登録済み: item_c 登録済み: item_d 登録済み: item_e === 名前が6文字以上の要素 === item_a(6文字) item_b(6文字) item_c(6文字) item_d(6文字) item_e(6文字)
よくあるミス
foreach 中にコレクションを変更する
foreach でイテレーション中のコレクション(List<T> など)に対して要素の追加・削除を行うと、InvalidOperationException が発生します。内部の列挙子(enumerator)がコレクションの変更を検出して例外をスローする仕組みになっています。
var names = new List<string> { "item_a", "item_b", "item_c" };
foreach (var name in names) {
if (name == "item_b") {
names.Remove(name);
}
}
要素を削除したい場合は、for 文を逆順で回すか、LINQ の Where() で条件に合わない要素を除外した新しいリストを作成してください。
for (int i = names.Count - 1; i >= 0; i--) {
if (names[i] == "item_b") {
names.RemoveAt(i);
}
}
names = names.Where(n => n != "item_b").ToList();
foreach のループ変数に代入しても元のコレクションは変わらない
foreach のループ変数は各要素のコピー(値型の場合)または参照のコピーです。ループ変数に値を代入しても、元のコレクションには反映されません。C# ではコンパイルエラーになります。
int[] scores = { 80, 90, 70 };
foreach (var score in scores) {
// score = score + 10; // コンパイルエラー: foreach のイテレーション変数には代入できません。
}
for (int i = 0; i < scores.Length; i++) {
scores[i] = scores[i] + 10; // 元の配列が更新されます。
}
概要
『C#』の foreach 文は IEnumerable / IEnumerable<T> を実装した型のすべての要素を先頭から末尾まで順に処理します。foreach 内のループ変数は各要素のコピー(値型)または参照のコピーであり、ループ変数に値を代入しても元のコレクションは変更されません。要素を書き換えたい場合は for 文でインデックスを使って直接代入してください。
foreach はインデックスを持たないため、「何番目の要素か」を知りたい場合は for 文・手動カウンタ・LINQ の Select((element, index) => ...) のいずれかを使ってください。逆順・ステップ変更が必要な場合も for 文が適しています(『for文』を参照)。
独自クラスでも GetEnumerator() メソッドを提供する IEnumerable<T> を実装すれば foreach に対応できます。また、LINQ の Where / Select などのメソッドも IEnumerable<T> を引数に取るため、foreach に対応した型はそのまま LINQ チェーンで利用できます(『Enumerable.Where』や『IEnumerable / IList』も合わせて参照してください)。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。