C#で配列やコレクションをすべて順番に処理したい時はforeach
が最も読みやすく安全です。
インデックスの管理を気にせず、要素を1つずつ取り出して処理できます。
本記事では、基本構文から配列、List、Dictionaryまで、初心者のかたにも分かりやすい実行例つきで解説します。
C#のforeach文の基本
foreachの書き方(基本構文)
foreach
は、列挙可能なコレクション(配列、List、Dictionaryなど)の全要素を順に取り出して処理します。
基本形は次のとおりです。
// 基本構文
foreach (要素の型 変数名 in コレクション)
{
// 変数名 を使った処理
}
実例として、整数配列を合計する簡単なサンプルを示します。
using System;
class Program
{
static void Main()
{
int[] nums = { 3, 5, 8 };
int sum = 0;
foreach (int n in nums) // nums の要素を1つずつ n に取り出す
{
sum += n;
}
Console.WriteLine($"合計: {sum}");
}
}
合計: 16
foreachのメリット(インデックス不要)
インデックスを意識せずに全要素を漏れなく処理できるのが大きな利点です。
境界チェックやインデックスのオフバイワン(±1のずれ)に悩まされにくく、コードの意図が明確になります。
特に辞書型のように「順序に意味が薄い」コレクションでは、for
よりforeach
の方が自然に読み取れます。
次の表は、for
とforeach
の使い分けのイメージです。
観点 | for | foreach |
---|---|---|
インデックスが必要な処理(位置指定の更新など) | 得意 | 不向き |
全要素の走査(読み取り中心) | 可 | 得意 |
コードの意図の明確さ | 普通 | 高い |
要素は読み取り専用(値の変更に注意)
foreachのループ変数は読み取り専用です。
次のように代入するとコンパイルエラーになります。
int[] a = { 1, 2, 3 };
foreach (var x in a)
{
// x = x * 2; // コンパイルエラー: foreach の変数 x は読み取り専用
}
値型配列(int[] など)の場合、ループ変数への変更は元の配列に反映されません。
要素を書き換えたいならインデックスを使うのが基本です。
using System;
class Program
{
static void Main()
{
int[] a = { 1, 2, 3 };
// 要素を書き換えたい場合は for でインデックスを使う
for (int i = 0; i < a.Length; i++)
{
a[i] *= 2;
}
foreach (var x in a)
{
Console.WriteLine(x);
}
}
}
2
4
6
一方、参照型要素(クラスのオブジェクトなど)では、オブジェクト自体のプロパティを変更することは可能です。
読み取り専用なのは「変数が指す参照(どのオブジェクトを指すか)」であり、オブジェクトの中身まで不変という意味ではありません。
using System;
using System.Collections.Generic;
class User
{
public string Name { get; set; }
}
class Program
{
static void Main()
{
var users = new List<User>
{
new User { Name = "Alice" },
new User { Name = "Bob" }
};
// 参照の差し替えは不可だが、プロパティの変更は可能
foreach (var u in users)
{
u.Name = u.Name.ToUpper(); // 中身の変更はOK
}
foreach (var u in users)
{
Console.WriteLine(u.Name);
}
}
}
ALICE
BOB
型の指定とvarの使い方
foreach
のループ変数は、要素の型を明示してもvar
で型推論しても構いません。
読みやすさと意図の明確さで選べば十分です。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var words = new List<string> { "C#", "foreach", "指南" };
// 型を明示
foreach (string w in words)
{
Console.WriteLine(w);
}
// var で型推論(この場合は string)
foreach (var w in words)
{
Console.WriteLine(w.ToUpper());
}
}
}
C#
foreach
指南
C#
FOREACH
指南
Dictionaryでは、var
を使うとKeyValuePair<TKey,TValue>
が自動で推論され、分解代入(タプルのようにキーと値を直接取り出す)も可能です。
これは後半のセクションで実演します。
配列をforeachで回す
配列(int[])のforeachの使い方
整数配列を走査して、合計や最大値を求める処理はforeach
が簡潔です。
using System;
class Program
{
static void Main()
{
int[] scores = { 72, 88, 95, 63 };
int sum = 0;
int max = int.MinValue;
foreach (int s in scores)
{
sum += s;
if (s > max) max = s;
}
double avg = (double)sum / scores.Length;
Console.WriteLine($"合計: {sum}");
Console.WriteLine($"平均: {avg:F1}");
Console.WriteLine($"最大: {max}");
}
}
合計: 318
平均: 79.5
最大: 95
文字列配列(string[])のforeach例
文字列配列から前後空白を除去し、空文字をスキップして表示する例です。
読み取り中心の処理にforeachはよく適合します。
using System;
class Program
{
static void Main()
{
string[] raw = { " apple", "banana ", " ", "Cherry" };
foreach (string s in raw)
{
string trimmed = s.Trim();
if (trimmed.Length == 0) continue; // 空はスキップ
Console.WriteLine(trimmed.ToLower());
}
}
}
apple
banana
cherry
Listをforeachで回す
List<T>をforeachで回す基本
List<T>
は配列と同様にforeach
でシンプルに扱えます。
ここでは偶数だけ数える例です。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var numbers = new List<int> { 1, 2, 3, 4, 6, 9 };
int evenCount = 0;
foreach (var n in numbers)
{
if (n % 2 == 0) evenCount++;
}
Console.WriteLine($"偶数の個数: {evenCount}");
}
}
偶数の個数: 3
ループ中の追加・削除は避ける
Listをforeach中に追加や削除を行うと、列挙子が無効になり例外が発生します。
安全に削除したい場合は、事前に削除対象を集めてから、ループの外でまとめて削除するか、forで後ろからインデックス削除を行います。
NG例(例外が発生):
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var list = new List<int> { 1, 2, 3, 4 };
try
{
foreach (var n in list)
{
if (n % 2 == 0)
{
list.Remove(n); // foreach 中の削除は危険
}
}
}
catch (InvalidOperationException ex)
{
Console.WriteLine("例外発生: " + ex.GetType().Name);
}
}
}
例外発生: InvalidOperationException
安全な方法1(別リストに集めてから削除):
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var list = new List<int> { 1, 2, 3, 4, 6 };
var toRemove = new List<int>();
foreach (var n in list)
{
if (n % 2 == 0) toRemove.Add(n);
}
foreach (var n in toRemove)
{
list.Remove(n);
}
Console.WriteLine(string.Join(",", list));
}
}
1,3
安全な方法2(forで後ろから削除):
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var list = new List<int> { 1, 2, 3, 4, 6 };
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i] % 2 == 0)
{
list.RemoveAt(i); // 後ろからならインデックスがずれない
}
}
Console.WriteLine(string.Join(",", list));
}
}
1,3
Dictionaryをforeachで回す
KeyValuePairでキーと値を取り出す
Dictionary<TKey,TValue>
は、foreach
でKeyValuePair<TKey,TValue>
を順に取り出せます。
さらにC# 7以降は分解によってキーと値を直接受け取れます。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var ages = new Dictionary<string, int>
{
["Alice"] = 30,
["Bob"] = 25,
["Carol"] = 27
};
// その1: KeyValuePair を明示
foreach (KeyValuePair<string, int> kv in ages)
{
Console.WriteLine($"{kv.Key} は {kv.Value} 歳");
}
Console.WriteLine("---");
// その2: 分解で直接 key と value を受け取る
foreach (var (name, age) in ages)
{
Console.WriteLine($"{name} は {age} 歳");
}
}
}
Alice は 30 歳
Bob は 25 歳
Carol は 27 歳
---
Alice は 30 歳
Bob は 25 歳
Carol は 27 歳
Keysだけ/Valuesだけをforeachで回す
キーだけ、値だけを走査したい場合はKeys
やValues
プロパティを使います。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var capitals = new Dictionary<string, string>
{
["Japan"] = "Tokyo",
["USA"] = "Washington, D.C.",
["France"] = "Paris"
};
Console.WriteLine("国名一覧:");
foreach (var country in capitals.Keys)
{
Console.WriteLine(country);
}
Console.WriteLine("----");
Console.WriteLine("首都一覧:");
foreach (var city in capitals.Values)
{
Console.WriteLine(city);
}
}
}
国名一覧:
Japan
USA
France
----
首都一覧:
Tokyo
Washington, D.C.
Paris
まとめ
foreachは「全要素の順次処理」を簡潔かつ安全に書ける基本構文です。
配列、List、Dictionaryのいずれでも、インデックスを意識せずに読み取り中心の処理ができます。
一方で、ループ変数は読み取り専用であり、ループ中のコレクションの追加・削除は避ける必要があります。
型指定はvar
での推論が便利で、Dictionaryでは分解を使うとキーと値を直接扱えます。
要素の更新や削除が必要な場面では、forでインデックスを使う、別途収集してからまとめて変更するといったパターンを選び、状況に応じて使い分けていきましょう。