C#におけるプログラミングにおいて、同じ処理を何度も繰り返す「ループ処理」は非常に重要な役割を果たします。

その中でも、特に繰り返し回数が明確に決まっている場合や、配列のインデックスを操作する場合に最も多用されるのが「for文」です。

for文を正しく理解し使いこなすことは、効率的なコードを書くための第一歩となります。

本記事では、C#のfor文の基本的な書き方から、より実践的な応用例、そして他の繰り返し文との使い分けまで、テクニカルな視点で詳しく解説します。

for文の基本構文と仕組み

C#のfor文は、決められた回数だけ処理を繰り返すための制御構造です。

まずは、その最も基本的な書き方とその動作フローを確認しましょう。

基本的な書き方

for文は、以下の3つの要素(初期化式、条件式、反復式)を括弧内に記述する構成となっています。

C#
for (初期化式; 条件式; 反復式)
{
    // 繰り返したい処理
}

各要素の役割は以下の通りです。

  1. 初期化式:ループを開始する前に一度だけ実行されます。通常はカウンタ変数の宣言と初期化を行います。
  2. 条件式:各ループの開始前に評価されます。この式が true の間、処理が継続されます。
  3. 反復式:ループ内の処理が終わるたびに実行されます。通常はカウンタ変数の値を増やしたり減らしたりします。

具体的なコード例

以下のプログラムは、0から4までの数字を順番にコンソールに出力するもっとも単純な例です。

C#
using System;

class Program
{
    static void Main()
    {
        // 0から4まで5回繰り返す
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("現在の値: " + i);
        }
    }
}
実行結果
現在の値: 0
現在の値: 1
現在の値: 2
現在の値: 3
現在の値: 4

この例では、最初に int i = 0 が実行され、i < 5 である限り、ループ内の処理が実行されます。

一周ごとに i++ (iを1増やす)が行われ、iが5になった時点で条件式が false となり、ループが終了します。

配列やリストの操作におけるfor文

実務においてfor文が最も活躍するのは、配列(Array)やリスト(List)の要素を順番に処理する場合です。

インデックス(添字)を直接指定できるため、柔軟な操作が可能です。

配列の全要素を走査する

配列のすべての要素にアクセスするには、配列の長さを示す Length プロパティを条件式に使用します。

C#
using System;

class Program
{
    static void Main()
    {
        string[] fruits = { "Apple", "Banana", "Orange", "Grape" };

        // 配列の要素数だけ繰り返す
        for (int i = 0; i < fruits.Length; i++)
        {
            Console.WriteLine($"インデックス {i} の果物: {fruits[i]}");
        }
    }
}
実行結果
インデックス 0 の果物: Apple
インデックス 1 の果物: Banana
インデックス 2 の果物: Orange
インデックス 3 の果物: Grape

ここで注意が必要なのは、条件式を i < fruits.Length とすることです。

配列のインデックスは0から始まるため、最大インデックスは「要素数 – 1」となります。

もし i <= fruits.Length と記述してしまうと、範囲外アクセス(IndexOutOfRangeException)が発生します。

リスト(List<T>)の操作

動的な配列である List<T> の場合は、Length ではなく Count プロパティを使用します。

C#
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 10, 20, 30 };

        for (int i = 0; i < numbers.Count; i++)
        {
            // 要素を2倍に更新する
            numbers[i] *= 2;
            Console.WriteLine($"更新後の値: {numbers[i]}");
        }
    }
}
実行結果
更新後の値: 20
更新後の値: 40
更新後の値: 60

ループの流れを制御する:breakとcontinue

for文の中では、特定の条件に応じてループを途中で抜けたり、現在の回をスキップしたりすることができます。

これには breakcontinue を使用します。

breakによるループの中断

break 文を使用すると、ループの途中であっても即座にループ全体を終了させることができます。

C#
using System;

class Program
{
    static void Main()
    {
        for (int i = 1; i <= 10; i++)
        {
            if (i == 6)
            {
                Console.WriteLine("6に到達したのでループを終了します。");
                break; 
            }
            Console.WriteLine(i);
        }
    }
}
実行結果
1
2
3
4
5
6に到達したのでループを終了します。

continueによる現在の回のスキップ

continue 文を使用すると、それ以降の処理を飛ばして、次の「反復式」へジャンプします。

つまり、特定の条件の時だけ処理を行いたくない場合に有効です。

C#
using System;

class Program
{
    static void Main()
    {
        for (int i = 1; i <= 5; i++)
        {
            if (i % 2 == 0)
            {
                // 偶数の場合はスキップ
                continue;
            }
            Console.WriteLine($"奇数: {i}");
        }
    }
}
実行結果
奇数: 1
奇数: 3
奇数: 5

多重ループ(入れ子のfor文)

for文の中にさらにfor文を記述することを「入れ子(ネスト)」と呼びます。

2次元配列の操作や、格子状のデータ処理を行う際によく用いられます。

九九の表を作成する例

以下のコードは、多重ループを利用して九九の計算結果を表示する例です。

外側のループが行を、内側のループが列を制御しています。

C#
using System;

class Program
{
    static void Main()
    {
        for (int i = 1; i <= 9; i++)
        {
            for (int j = 1; j <= 9; j++)
            {
                // 書式指定で桁を揃えて出力
                Console.Write($"{i * j, 3}");
            }
            // 行が終わるたびに改行
            Console.WriteLine();
        }
    }
}
実行結果
  1  2  3  4  5  6  7  8  9
  2  4  6  8 10 12 14 16 18
  3  6  9 12 15 18 21 24 27
  4  8 12 16 20 24 28 32 36
  5 10 15 20 25 30 35 40 45
  6 12 18 24 30 36 42 48 54
  7 14 21 28 35 42 49 56 63
  8 16 24 32 40 48 56 64 72
  9 18 27 36 45 54 63 72 81

多重ループを使用する際は、ループの回数が「外側の回数 × 内側の回数」となるため、計算量が急激に増える可能性に注意が必要です。

三重、四重と深くしすぎると、パフォーマンス低下の原因となります。

特殊なfor文の記述方法

C#のfor文は非常に柔軟であり、基本の形式以外にもいくつかの書き方が存在します。

複数の変数を制御する

初期化式や反復式に、カンマ区切りで複数の式を記述することが可能です。

C#
for (int i = 0, j = 10; i <= 10; i++, j--)
{
    Console.WriteLine($"i: {i}, j: {j}");
}

この例では、iを増やしながら同時にjを減らしていくループを1つのfor文で実現しています。

無限ループ

条件式を省略(あるいは常に true に)すると、無限ループになります。

通常は while(true) が使われますが、for文でも以下のように記述できます。

C#
for (;;)
{
    // 永久に繰り返される
    // 内部で break しない限り終わらない
}

逆順ループ(カウントダウン)

反復式でデクリメント(--)を使用することで、大きな値から小さな値へとループさせることも容易です。

C#
for (int i = 10; i > 0; i--)
{
    Console.WriteLine($"カウントダウン: {i}");
}

for文とforeach文、while文の使い分け

C#には他にも繰り返し処理を行う構文があります。

これらをどのように使い分けるべきか、以下の表にまとめました。

構文主な用途特徴
for回数指定、インデックス操作カウンタ変数を自由に操作でき、柔軟性が高い。
foreachコレクションの全要素参照読み取り専用で安全。インデックスを意識せず簡潔に書ける。
while条件を満たす間の繰り返し繰り返し回数が不明で、特定の状態になるまで続けたい時に最適。
do-while最低1回は実行する繰り返し条件判定が処理の後にあるため、必ず1回は実行される。

for文を選ぶべきシーン

「現在のループが何回目か」という情報が必要な場合や、「要素を1つ飛ばしで処理したい」「特定のインデックスの値を書き換えたい」といった場合には、for文が最適です。

一方で、単にリストの全要素を順番に表示するだけであれば、foreach 文の方がコードが簡潔になり、バグも混入しにくくなります。

for文におけるパフォーマンスとベストプラクティス

大規模なデータ処理を行う場合、for文の書き方一つで実行速度が変わることがあります。

ここでは現代的なC#開発における注意点を解説します。

配列の長さを変数にキャッシュすべきか?

古いプログラミングの格言では、以下のようにループの前に長さを変数に代入することが推奨されていました。

C#
int len = largeArray.Length;
for (int i = 0; i < len; i++) { ... }

しかし、現在のC#(.NET)コンパイラおよびJITコンパイラは非常に賢いため、i < array.Length と記述しても最適化が行われます。

むしろ、直接 Length を書くことで「境界チェックの省略(Range Check Elimination)」という最適化が働きやすくなり、パフォーマンスが向上する場合が多いです。

Span<T> を利用した高速化

C# 7.2以降で導入された Span<T>ReadOnlySpan<T> は、メモリ効率を極限まで高めるための構造体です。

大量のデータの一部を切り出してループ処理する場合、従来の配列のコピーではなく Span を使うことで、ガベージコレクションの発生を抑えつつ高速に処理できます。

C#
ReadOnlySpan<int> span = new int[] { 1, 2, 3, 4, 5 }.AsSpan();
for (int i = 0; i < span.Length; i++)
{
    // 配列と同じように高速アクセス可能
    int val = span[i];
}

よくある間違いとトラブルシューティング

for文の実装において初心者が陥りやすいミスとその対策を紹介します。

1. 無限ループ(条件式のミス)

反復式を書き忘れたり、条件式が常に真になるように書いてしまうと、プログラムがフリーズしたり、メモリを使い果たしたりします。

C#
// 誤った例:iを増やしていないため終わらない
for (int i = 0; i < 10; ) 
{
    Console.WriteLine(i);
}

2. 1違いのエラー(Off-by-one error)

ループの回数が1回多かったり少なかったりするミスです。

  • i < 10 (0から9までの10回)
  • i <= 10 (0から10までの11回)

配列のインデックスを扱う際は、特に 「<」と「<=」の使い分けに細心の注意を払ってください。

3. ループ内でのコレクション操作

List<T> をfor文で回している最中に、そのリスト自体の要素を削除(Remove)すると、インデックスがズレてしまい、意図しない要素をスキップしたりエラーになったりします。

要素を削除する場合は、後ろから(逆順に)ループを回すのが定石です。

C#
// 正しい削除方法:後ろから回す
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i].ShouldDelete)
    {
        list.RemoveAt(i);
    }
}

実践応用:for文を使ったアルゴリズム

最後に、少しだけ複雑な応用例として、データの集計処理を見てみましょう。

合計値と平均値の算出

数値配列の中から、特定の条件を満たす値だけを合計し、その平均を求める処理です。

C#
using System;

class Program
{
    static void Main()
    {
        int[] scores = { 80, 45, 92, 67, 30, 88 };
        int sum = 0;
        int count = 0;

        for (int i = 0; i < scores.Length; i++)
        {
            // 60点以上の合格者のみ集計
            if (scores[i] >= 60)
            {
                sum += scores[i];
                count++;
            }
        }

        double average = count > 0 ? (double)sum / count : 0;

        Console.WriteLine($"合格者数: {count}");
        Console.WriteLine($"合格者の平均点: {average:F1}");
    }
}
実行結果
合格者数: 4
合格者の平均点: 81.8

このように、for文の中に条件分岐(if文)を組み合わせることで、複雑なビジネスロジックを実装することができます。

まとめ

C#のfor文は、プログラムにおける繰り返し処理の基本でありながら、非常に強力で柔軟なツールです。

  • 初期化・条件・反復の3要素を理解し、正確な回数を制御する。
  • 配列やリストのインデックス操作において、LengthCount を正しく活用する。
  • breakやcontinueでループの流れを細かく制御する。
  • パフォーマンスが求められる場面では、Span<T> などの最新機能も検討する。

これらのポイントを意識することで、より読みやすく、かつ効率的なコードを書くことができるようになります。

まずは基本的なカウントアップから始め、徐々に多重ループやコレクション操作などの応用へとステップアップしていきましょう。

繰り返し処理をマスターすることは、複雑なアルゴリズムを構築する上での揺るぎない土台となります。