【C#】LINQのRepeatメソッドで同じ値のコレクションを効率的に生成する

配列やリストを初期化する際、「すべての要素を0で埋めたい」「特定のエラーメッセージで埋め尽くされたリストを作りたい」といった、同一の値を繰り返すコレクションが必要になる場面があります。

forループを使用して一つずつ値を代入することも可能ですが、C#のLINQに含まれるEnumerable.Repeatメソッドを使用することで、初期化ロジックを簡潔かつ宣言的に記述できます。

今回は、週間スケジュール表の初期化処理を題材に、Repeatメソッドの活用法と、参照型を扱う際の重要な注意点について解説します。


目次

Enumerable.Repeatメソッドの概要

Enumerable.Repeatは、指定した要素を、指定した回数だけ繰り返したシーケンス(IEnumerable<T>)を生成します。

構文は以下の通りです。

Enumerable.Repeat(T element, int count)
  • element: 繰り返したい値(またはオブジェクト)。
  • count: 繰り返す回数。

実践的なコード例:スケジュールの初期化

以下のコードは、1週間(7日分)のスケジュール枠を確保し、すべてを「未定(Pending)」という初期ステータスで埋める例です。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ScheduleSystem
{
    class Program
    {
        static void Main()
        {
            // シナリオ:
            // 新しい週のスケジュールを作成する際、
            // 月曜日から日曜日までの7日分を一旦「未定」で初期化したい。

            string defaultStatus = "未定";
            int daysOfWeek = 7;

            // 1. Enumerable.Repeatを使用して、同じ文字列を7つ生成
            // 結果: "未定", "未定", "未定", ... (7個)
            // 配列として扱いたいため、最後にToArray()を呼び出します。
            string[] weeklySchedule = Enumerable.Repeat(defaultStatus, daysOfWeek).ToArray();

            // 2. 結果の確認
            Console.WriteLine("--- 週間スケジュール初期値 ---");
            
            // インデックス付きで出力(Selectのオーバーロードを利用)
            var displayList = weeklySchedule.Select((status, index) => 
                $"Day {index + 1}: {status}");

            foreach (var item in displayList)
            {
                Console.WriteLine(item);
            }
        }
    }
}

実行結果

--- 週間スケジュール初期値 ---
Day 1: 未定
Day 2: 未定
Day 3: 未定
Day 4: 未定
Day 5: 未定
Day 6: 未定
Day 7: 未定

技術的なポイントと重要な注意点

1. Rangeとの使い分け

数値を扱う場合、Enumerable.Rangeと混同しやすいですが、明確な違いがあります。

  • Range: 1, 2, 3...値が変化する連番を生成します。
  • Repeat: 1, 1, 1...同じ値を繰り返します。

2. 【最重要】参照型をRepeatする場合の挙動

Enumerable.Repeatを使用する際、最も注意すべき点は「オブジェクト(参照型)」を繰り返す場合です。 Repeatは引数で渡された「インスタンスへの参照」をそのまま繰り返します。つまり、すべての要素が「同一のインスタンス」を指すことになります。

// 【悪い例】
// 1つのPersonインスタンスが5回繰り返されるだけです。
var people = Enumerable.Repeat(new Person(), 5).ToList();

// 1つ目の名前を変更すると...
people[0].Name = "田中";

// すべての要素の名前が "田中" に変わってしまいます(同じ実体を見ているため)。
Console.WriteLine(people[1].Name); // "田中" と出力される

3. 個別のインスタンスを生成したい場合

もし、「5人の異なる(独立した)Personインスタンス」でリストを初期化したい場合は、Repeatではなく、RangeSelectを組み合わせる方法が推奨されます。

// 【良い例】
// Selectの中で毎回 new されるため、それぞれ独立したインスタンスになります。
var distinctPeople = Enumerable.Range(0, 5)
                               .Select(_ => new Person())
                               .ToList();

まとめ

Enumerable.Repeatは、初期値埋め(パディング)や、固定値のテストデータを生成する際に非常に便利です。文字列や数値(値型・不変な型)を扱う場合はRepeatを使用し、変更可能なオブジェクト(参照型)のリストを初期化したい場合はRangeSelectを組み合わせる、という使い分けを意識することで、バグのない堅牢なコードを記述できます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次