今日、C#で次のことができることに驚きました。
List<int> a = new List<int> { 1, 2, 3 };
なぜこれを行うことができますか?どのコンストラクターが呼び出されますか?自分のクラスでこれを行うにはどうすればよいですか?私はこれが配列を初期化する方法であることを知っていますが、配列は言語項目であり、リストは単純なオブジェクトです...
これは、.NETのコレクション初期化子構文の一部です。次の条件を満たしている限り、作成するコレクションでこの構文を使用できます。
IEnumerable
(できれば_IEnumerable<T>
_を実装)
Add(...)
という名前のメソッドがあります
何が起こるかは、デフォルトのコンストラクターが呼び出され、初期化子の各メンバーに対してAdd(...)
が呼び出されます。
したがって、これら2つのブロックはほぼ同じです。
_List<int> a = new List<int> { 1, 2, 3 };
_
そして
_List<int> temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
List<int> a = temp;
_
必要に応じて、can代替コンストラクターを呼び出すことができます。たとえば、成長中に_List<T>
_が大きくなりすぎないようにするためなどです。
_// Notice, calls the List constructor that takes an int arg
// for initial capacity, then Add()'s three items.
List<int> a = new List<int>(3) { 1, 2, 3, }
_
Add()
メソッドは単一の項目を取る必要がないことに注意してください。たとえば、_Dictionary<TKey, TValue>
_のAdd()
メソッドは2つの項目を取ります。
_var grades = new Dictionary<string, int>
{
{ "Suzy", 100 },
{ "David", 98 },
{ "Karen", 73 }
};
_
ほぼ同じです:
_var temp = new Dictionary<string, int>();
temp.Add("Suzy", 100);
temp.Add("David", 98);
temp.Add("Karen", 73);
var grades = temp;
_
したがって、これを独自のクラスに追加するには、前述のように、IEnumerable
(再度、できれば_IEnumerable<T>
_)を実装し、1つ以上のAdd()
メソッドを作成するだけです。
_public class SomeCollection<T> : IEnumerable<T>
{
// implement Add() methods appropriate for your collection
public void Add(T item)
{
// your add logic
}
// implement your enumerators for IEnumerable<T> (and IEnumerable)
public IEnumerator<T> GetEnumerator()
{
// your implementation
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
_
次に、BCLコレクションと同じように使用できます。
_public class MyProgram
{
private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };
// ...
}
_
(詳細については、 [〜#〜] msdn [〜#〜] を参照してください)
いわゆる 構文糖 です。
List<T>
は「単純な」クラスですが、コンパイラーはあなたの生活を楽にするために特別な扱いをします。
これはいわゆる collection initializer です。 IEnumerable<T>
およびAdd
メソッドを実装する必要があります。
C#バージョン3.0仕様 「コレクション初期化子が適用されるコレクションオブジェクトは、1つのTに対してSystem.Collections.Generic.ICollectionを実装するタイプでなければなりません。」
ただし、この情報は、この記事の執筆時点では不正確であると思われます。以下のコメントのEric Lippertの説明を参照してください。
コレクション初期化子のもう1つの素晴らしい点は、Add
メソッドのオーバーロードを複数持つことができ、それらをすべて同じ初期化子で呼び出すことができることです!たとえば、これは動作します:
public class MyCollection<T> : IEnumerable<T>
{
public void Add(T item, int number)
{
}
public void Add(T item, string text)
{
}
public bool Add(T item) //return type could be anything
{
}
}
var myCollection = new MyCollection<bool>
{
true,
{ false, 0 },
{ true, "" },
false
};
正しいオーバーロードを呼び出します。また、Add
という名前のメソッドだけを探します。戻り値の型は何でもかまいません。
collection initializers のおかげで動作します。これは基本的に、Addメソッドを実装するためにコレクションを必要とし、それがあなたのために仕事をします。
配列のような構文は、一連のAdd()
呼び出しで有効にされています。
これをもっと興味深い例で見るために、C#で最初は違法と思われる2つの興味深いことを行う次のコードを考えてみてください。
_public class MyClass
{
public MyClass()
{
_list = new List<string>();
}
private IList<string> _list;
public IList<string> MyList
{
get
{
return _list;
}
}
}
//In some other method
var sample = new MyClass
{
MyList = {"a", "b"}
};
_
このコードは完全に機能しますが、1)MyListは読み取り専用であり、2)配列初期化子でリストを設定します。
これが機能する理由は、オブジェクト初期化子の一部であるコードでは、コンパイラが常に_{}
_のような構文を読み取り専用フィールドでも完全に正当な一連のAdd()
呼び出しに変換するためです。 。