web-dev-qa-db-ja.com

ListをC#の配列のように初期化できるのはなぜですか?

今日、C#で次のことができることに驚きました。

List<int> a = new List<int> { 1, 2, 3 };

なぜこれを行うことができますか?どのコンストラクターが呼び出されますか?自分のクラスでこれを行うにはどうすればよいですか?私はこれが配列を初期化する方法であることを知っていますが、配列は言語項目であり、リストは単純なオブジェクトです...

131

これは、.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 [〜#〜] を参照してください)

183

いわゆる 構文糖 です。

List<T>は「単純な」クラスですが、コンパイラーはあなたの生活を楽にするために特別な扱いをします。

これはいわゆる collection initializer です。 IEnumerable<T>およびAddメソッドを実装する必要があります。

11
Krizz

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という名前のメソッドだけを探します。戻り値の型は何でもかまいません。

6
nawfal

collection initializers のおかげで動作します。これは基本的に、Addメソッドを実装するためにコレクションを必要とし、それがあなたのために仕事をします。

6
vc 74

配列のような構文は、一連の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()呼び出しに変換するためです。 。

0
yoel halb