配列やリストを初期化する際、「すべての要素を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ではなく、RangeとSelectを組み合わせる方法が推奨されます。
// 【良い例】
// Selectの中で毎回 new されるため、それぞれ独立したインスタンスになります。
var distinctPeople = Enumerable.Range(0, 5)
.Select(_ => new Person())
.ToList();
まとめ
Enumerable.Repeatは、初期値埋め(パディング)や、固定値のテストデータを生成する際に非常に便利です。文字列や数値(値型・不変な型)を扱う場合はRepeatを使用し、変更可能なオブジェクト(参照型)のリストを初期化したい場合はRangeとSelectを組み合わせる、という使い分けを意識することで、バグのない堅牢なコードを記述できます。
