C#の初期化子(3)-コレクション初期化子
《 初回公開:2020/06/28 , 最終更新:未 》
visual studio2019 C#8.0および一部C#7.3で動作確認。
【 目次 】
コレクションクラス
List型やDictionary型等の複数のデータの集まりを示すクラスをコレクションクラスと呼ぶ。
配列のスーパクラスであるArrayクラスも含めてコレクションクラスは一般的にICollection インターフェイスを実装しておりICollectionインターフェイスはIEnumerableインターフェイスを継承している。
コレクション初期化子
コレクション初期化子(Collection initializers)を使うとコレクションオブジェクトの生成時にコレクションの要素を追加できる。
代表的なコレクションオブジェクトの初期化の例としてListとDictionaryの利用例を示す。
Listクラスのコレクション初期化子
Listクラスのコレクション初期化子の例
var tokyo_TV_Chanels = new List<int> { 1, 2, 4, 5, 6, 7, 8 }; Console.WriteLine($"{string.Join(", ", tokyo_TV_Chanels)}");
実行結果
1, 2, 4, 5, 6, 7, 8
上記のコードの1行目のコードは次のコードと等価である。
List<int> tokyo_TV_Chanels = new List<int>(); tokyo_TV_Chanels.Add(1); tokyo_TV_Chanels.Add(2); tokyo_TV_Chanels.Add(4); tokyo_TV_Chanels.Add(5); tokyo_TV_Chanels.Add(6); tokyo_TV_Chanels.Add(7); tokyo_TV_Chanels.Add(8);
つまり、オブジェクトの生成と同時にコレクションの要素を追加している事になる。
もう一つListクラスのコレクション初期化子の例を、
次のようなクラスを用意して
class Person { public string Name { get; set; } public int Age { get; set; } public void ShowMe() { Console.WriteLine($"Person: Name={Name}, Age={Age}"); } }
コレクション初期化子とオブジェクト初期化子を組み合わせて
var persons = new List<Person> { new Person { Name = "愚鈍人", Age = 999 }, new Person { Name = "妻", Age = 17 }, new Person { Name = "子", Age = 10 }, null, }; foreach (var person in persons) { if (person == null) { Console.WriteLine("person is null"); } else { person.ShowMe(); } }
実行結果
CPerson2: Name=愚鈍人, Age=999 CPerson2: Name=妻, Age=17 CPerson2: Name=子, Age=10 person is null
Dictionaryクラスのコレクション初期化子
Dictionaryクラスのコレクション初期化子の例
ListクラスのようにAddメソッドに一つの引数しかとらない場合と異なり、DictionaryクラスのAddメソッドの場合は複数の引数をとる。
この場合は複数の引数を,
(カンマ)で区切って、それを{}
(中かっこ)で囲む。
var numbers = new Dictionary<int, string> { {7, "seven" }, { 9, "nine" }, { 13,"thirteen" }, }; foreach (KeyValuePair<int, string> kvp in numbers) Console.WriteLine($"numbers[{kvp.Key}] = {kvp.Value}");
実行結果
numbers[7] = seven numbers[9] = nine numbers[13] = thirteen
インデックサによる初期化子
コレクションクラスにインデックサが実装されていて、初期化時にインデックサが読み取り/書き込み可能な場合、インデックス付きの要素を指定できる。
var numbers = new Dictionary<int, string> { [7] = "seven", [9] = "nine", [13] = "thirteen" };
Listクラスも読み取り/書き込み可能なインデクサを備えているが、Listクラスの場合は初期化子としてインデクサを指定する事はできない。
コンパイルエラーにはならないが、実行時エラー(System.ArgumentOutOfRangeException)となってしまう。
このコードは実行時エラーとなる
var tokyo_TV_Chanels = new List<int>() { [0] = 1, [1] = 4, [2] = 5 };
これは次のコードがエラーになる事と同じ理由によるものと思われる。
Addメソッドによって要素が追加されていないのに、インデックサを使って追加されていない要素に対してアクセスする事ができない事による。
このコードも実行時エラーとなる
var tokyo_TV_Chanels = new List<int>(100); tokyo_TV_Chanels[0] = 1; tokyo_TV_Chanels[1] = 4; tokyo_TV_Chanels[2] = 5;
コレクション初期化子を指定できるクラスの実装
コレクション初期化子を使えるための要件は、IEnumerableを実装しており,かつAddメソッドが使える事となっている。
- オブジェクト初期化子とコレクション初期化子 - C# プログラミング ガイド | Microsoft Docs
コレクション初期化子を使うと、IEnumerable を実装するコレクション型を初期化するときに 1 つ以上の要素の初期化子を指定でき、適切なシグネチャの Add をインスタンス メソッドまたは拡張メソッドとして使用できます。
要素の初期化子は、単純な値、式またはオブジェクト初期化子です。 コレクション初期化子を使用すると、呼び出しを複数回指定する必要がなくなります。
コンパイラによって呼び出しが自動的に追加されるためです。 - 小ネタ コレクション初期化子 | ++C++; // 未確認飛行 C ブログ
このコレクション初期化を使える条件は、Add メソッドを持っていて、かつ、 IEnumerable を実装していることです。
...
で、コレクション初期化子、IEnumerable 要るの?
たぶん、誤用の防止
配列のスーパクラスであるArrayクラスはIEnumerableインターフェースを備えているが、要素数が固定なためAddメソッドを備えておらず,コレクション初期化子が使えないはずであるが、配列の初期化子を用いておなじような構文が使える。
var iArray = new int[] { 1, 2, 4, 5, 6, 7, 8 }; var strArray = new string[] { "NHK", "Eテレ", "日テレ", "テレ朝", "TBS", "テレ東", "フジ" }; var personArray = new Person[] { new Person { Name = "愚鈍人", Age = 999 }, new Person { Name = "妻", Age = 17 }, new Person { Name = "子", Age = 10 }, };
コレクション初期化子を指定できるクラスを記述してみる。
コレクション初期化子を指定できるクラスのコード
class MyEnumerable : IEnumerable<string> { public IEnumerator<string> GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } private string _allString; public string AllString { get => _allString; } public MyEnumerable(string allString) { _allString = allString; } public void Add(string s) { _allString += s; } public void ShowMe() => Console.WriteLine($"MyEnumerable: AllString={AllString}"); }
コレクション初期化子を使えるようにするにはIEnumerableインターフェースを実装している必要があるが、foreachループを使わないのであればIEnumerableインターフェース必須のGetEnumeratorメソッドの中身を実際に実装する必要はない。
上記のクラスのAddメソッドは、ただ文字列を連結するだけの意味のないものである。
コレクション初期化子を指定する際にコンストラクタも指定する例も示したかったので、コンストラクタも実装している。
コレクション初期化子を指定してMyEnumerableクラスのオブジェクトを生成。
new MyEnumerable("あいうえお") { "かきくけこ", "さしすせそ" }.ShowMe();
実行結果
MyEnumerable: AllString=あいうえおかきくけこさしすせそ