閉じる

C#のforeach文の使い方: 基本から配列・List・Dictionary

C#で配列やコレクションをすべて順番に処理したい時はforeachが最も読みやすく安全です。

インデックスの管理を気にせず、要素を1つずつ取り出して処理できます。

本記事では、基本構文から配列、List、Dictionaryまで、初心者のかたにも分かりやすい実行例つきで解説します。

C#のforeach文の基本

foreachの書き方(基本構文)

foreachは、列挙可能なコレクション(配列、List、Dictionaryなど)の全要素を順に取り出して処理します。

基本形は次のとおりです。

C#
// 基本構文
foreach (要素の型 変数名 in コレクション)
{
    // 変数名 を使った処理
}

実例として、整数配列を合計する簡単なサンプルを示します。

C#
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の方が自然に読み取れます。

次の表は、forforeachの使い分けのイメージです。

観点forforeach
インデックスが必要な処理(位置指定の更新など)得意不向き
全要素の走査(読み取り中心)得意
コードの意図の明確さ普通高い

要素は読み取り専用(値の変更に注意)

foreachのループ変数は読み取り専用です

次のように代入するとコンパイルエラーになります。

C#
int[] a = { 1, 2, 3 };
foreach (var x in a)
{
    // x = x * 2; // コンパイルエラー: foreach の変数 x は読み取り専用
}

値型配列(int[] など)の場合、ループ変数への変更は元の配列に反映されません。

要素を書き換えたいならインデックスを使うのが基本です。

C#
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

一方、参照型要素(クラスのオブジェクトなど)では、オブジェクト自体のプロパティを変更することは可能です。

読み取り専用なのは「変数が指す参照(どのオブジェクトを指すか)」であり、オブジェクトの中身まで不変という意味ではありません

C#
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で型推論しても構いません。

読みやすさと意図の明確さで選べば十分です。

C#
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が簡潔です。

C#
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はよく適合します

C#
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でシンプルに扱えます。

ここでは偶数だけ数える例です。

C#
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例(例外が発生):

C#
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(別リストに集めてから削除):

C#
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で後ろから削除):

C#
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>は、foreachKeyValuePair<TKey,TValue>を順に取り出せます。

さらにC# 7以降は分解によってキーと値を直接受け取れます。

C#
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で回す

キーだけ、値だけを走査したい場合はKeysValuesプロパティを使います。

C#
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でインデックスを使う、別途収集してからまとめて変更するといったパターンを選び、状況に応じて使い分けていきましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

C#の入門記事を中心に、開発環境の準備からオブジェクト指向の基本まで、順を追って解説しています。ゲーム開発や業務アプリを目指す人にも役立ちます。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!