C#

リストや配列を生成する際、すべての要素を特定の値で初期化したい

最近はC#に凝っています。LINQとvarが最高すぎて、Javaにはもどれない(´・ω・`)

リストや配列を生成する際、すべての要素を特定の値で初期化したいという場合、プログラミング言語の種類によっては専用のコンストラクタが用意されているものもありますが、C#ではEnumerable#Repaeatを利用する方法がよさそうです。

using System;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        var list = Enumerable.Repeat("a", 20).ToList();

        var str = "[" + string.Join(",", list) + "]";
        Console.WriteLine(str);
        // => [a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a]
    }
}

注意すべきは第1引数の値が要素ごとに複製されないということです。intstringのようなimmutableなクラスでは問題ありませんが、この方法によりmutableなオブジェクトで配列やリストを初期化したいという場合には気を付ける必要があります。以下のサンプルコードでは、mutableなクラスのうち、もっとも代表的なもののひとつSystem.Collections.Generic.Listを例として、この方法が想定通り動作しないことを示しています。

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

class Program
{
    static void Main(string[] args)
    {
        var matrix = Enumerable.Repeat(new List<string>() { "a", "a" }, 2).ToList();
        matrix[0][0] = "b";

        var str = matrix.Aggregate("", (sum, row) => sum + "[" + string.Join(",", row) + "]");
        Console.WriteLine(str);
        // 想定していた値: [b,a][a,a]
        // 実際の値: [b,a][b,a]
        // ===> new List<string>() { "a", "a" }が複製されていないことが確認できます。
    }
}

ちなみにこのサンプルコードを想定通りに機能させる場合は、以下のような初期化方法、つまりRepeatではなくRangeSelectを組み合わせる方法を採用すべきです。

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

class Program
{
    static void Main(string[] args)
    {
        var matrix = Enumerable.Range(0, 2).Select((x) => new List<string>() { "a", "a" }).ToList();
        matrix[0][0] = "b";


        var str = matrix.Aggregate("", (sum, row) => sum + "[" + string.Join(",", row) + "]");
        Console.WriteLine(str);
        // 想定していた値 => [b,a][a,a]
        // 実際の値       => [b,a][a,a]
    }
}