web-dev-qa-db-ja.com

C#コレクション初期化子がこのように機能するのはなぜですか?

私はC#コレクションの初期化子を調べていましたが、実装は非常に実用的であると同時に、C#の他のものとは非常に異なっていることがわかりました。

私はこのようなコードを作成することができます:

using System;
using System.Collections;

class Program
{
    static void Main()
    {
        Test test = new Test { 1, 2, 3 };
    }
}

class Test : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public void Add(int i) { }
}

コンパイラの最小要件(IEnumerablepublic void Addを実装)を満たしているので、これは機能しますが、明らかに価値がありません。

C#チームがより厳格な一連の要件を作成できなかった理由は何でしょうか。言い換えると、この構文をコンパイルするために、コンパイラーが型にICollectionを実装する必要がないのはなぜですか?これは、他のC#機能の精神に基づいているようです。

57
Andrew Hare

あなたの観察は的を射ています-実際、それはMicrosoft C#言語PMであるMadsTorgersenによって作成されたものを反映しています。

マッドは2006年10月にこのテーマについてコレクションとは?というタイトルで投稿しました。

確かに、System.Collections.ICollectionを使用したフレームワークの最初のバージョンでそれを吹き飛ばしました。これはほとんど役に立たないものです。しかし、ジェネリックが.NET Framework 2.0で登場したとき、私たちはそれをかなりうまく修正しました。System.Collections.Generic.ICollection<T>を使用すると、要素の追加と削除、列挙、カウント、メンバーシップの確認を行うことができます。

明らかにそれ以降、誰もがコレクションを作成するたびにICollection <T>を実装しますよね?そうではありません。これが、LINQを使用してコレクションが実際に何であるかを学習する方法と、それによってC#3.0の言語設計がどのように変更されたかを示しています。

フレームワークには_ICollection<T>_の実装は14しかありませんが、IEnumerableを実装し、パブリックAdd()メソッドを持つ189のクラスがあることがわかりました。

このアプローチには隠れた利点があります。_ICollection<T>_インターフェースに基づいていたとしたら、サポートされているAdd()メソッドは1つだけでした。

対照的に、彼らが採用したアプローチは、コレクションの初期化子がAdd()メソッドの引数のセットを形成することを意味します。

説明のために、コードを少し拡張してみましょう。

_class Test : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public void Add(int i) { }

    public void Add(int i, string s) { }
}
_

あなたは今これを書くことができます:

_class Program
{
    static void Main()
    {
        Test test 
            = new Test 
            {
                1, 
                { 2, "two" },
                3 
            };
    }
}
_
90
Bevan

私もこれについて考えましたが、私が最も満足する答えは、ICollectionにはAdd以外にも、Clear、Contains、CopyTo、Removeなどの多くのメソッドがあるということです。要素の削除やクリアは、オブジェクト初期化構文をサポートできることとは関係ありません。必要なのはAdd()だけです。

フレームワークが十分にきめ細かく設計されていて、ICollectionAddインターフェイスがあれば、「完璧な」設計になります。しかし、正直なところ、インターフェイスごとに1つのメソッドがあるので、それが大きな価値をもたらすとは思いません。 IEnumerable + Addはハックなアプローチのように見えますが、考えてみると、より良い代替手段です。

編集:C#がこのタイプのソリューションの問題に取り組んだのはこれだけではありません。 .NET 1.1以降、foreachはダックタイピングを使用してコレクションを列挙するため、クラスで実装する必要があるのはGetEnumerator、MoveNext、およびCurrentだけです。 Kirill Osenkovには post があり、これもあなたの質問をします。

9
DavidN

(私はこれに3年遅れていることを知っていますが、既存の回答には満足していませんでした。)

なぜ、この構文をコンパイルするために、コンパイラは型がICollectionを実装する必要がないのですか?

私はあなたの質問を逆にします:コンパイラが本当に必要とされていない要件を持っていたら、それはどのような用途になりますか?

ICollection以外のクラスも、コレクション初期化構文の恩恵を受けることができます。以前に追加されたデータへのアクセスを許可せずに、データを追加できるクラスを検討してください。

個人的には、new Data { { ..., ... }, ... }構文を使用して、単体テストのコードにDSLのような軽い外観を追加するのが好きです。

実際には、要件を弱めたいので、IEnumerableをわざわざ実装しなくても、見栄えの良い構文を使用できます。コレクション初期化子はAdd()の純粋な構文糖衣構文であり、他に何も必要ありません。

3