21

Dictionary<T1,T2>次のように初期化できるのはなぜですか。

var dict = new Dictionary<string,int>() { 
    { "key1", 1 },
    { "key2", 2 }
};

...ただし、たとえば、オブジェクトの配列をKeyValuePair<T1,T2>まったく同じ方法で初期化することはできません。

var kvps = new KeyValuePair<string,int>[] {
    { "key1", 1 },
    { "key2", 2 }
};
// compiler error: "Array initializers can only be used in a variable 
// or field initializer.  Try using a new expression instead."

new KeyValuePair<string,int>() { "key1", 1 }アイテムごとになどを書くだけで、2番目の例を機能させることができると思います。しかし、最初の例で可能なのと同じタイプの簡潔な構文を使用できるかどうか疑問に思っています。

それが不可能な場合、Dictionaryタイプが特別な理由は何ですか?

4

6 に答える 6

33

Addコレクション初期化子の構文は、適切な数のパラメーターを使用してへの呼び出しに変換されます。

var dict = new Dictionary<string,int>();
dict.Add("key1", 1);
dict.Add("key2", 2);

Addこの特別な初期化構文は、メソッドと実装を持つ他のクラスでも機能しますIEnumerableDictionary特別なことは何もなく、この構文が適切なクラスで機能することを証明するために、完全にクレイジーなクラスを作成しましょう。

// Don't do this in production code!
class CrazyAdd : IEnumerable
{
    public void Add(int x, int y, int z)
    {
        Console.WriteLine(x + y + z); // Well it *does* add...
    }

    public IEnumerator GetEnumerator() { throw new NotImplementedException(); }
}

これで、次のように記述できます。

var crazyAdd = new CrazyAdd
{
    {1, 2, 3},
    {4, 5, 6}
};

出力:

6
15

オンラインでの動作を確認してください: ideone

あなたが尋ねた他のタイプについては:

  • メソッドがないため、配列では機能しませんAdd
  • List<T>にはAddメソッドがありますが、パラメーターは 1 つしかありません。
于 2012-06-24T01:47:16.080 に答える
11

Add2 つの引数を取るオーバーロード for があるため、ディクショナリで動作します。配列にはメソッドさえありませんAdd。2 つの引数を持つメソッドは言うまでもありません。

このDictionaryクラスは、内部で動作するように特別に設計されてKeyValuePair<,>います。これが、コンストラクターを手動で呼び出す必要がない唯一の理由です。代わりに、2 つの引数Addが呼び出され、内部で KeyValuePair を構築します。

他のすべてIEnumerable<KeyValuePair<,>>にはこの特別な実装がないため、次のように初期化する必要があります。

var array = new KeyValuePair<int, int>[] {
    new KeyValuePair<int, int>(1, 2),
    new KeyValuePair<int, int>(3, 4)
};

これを実装するとしましょう:

class ThreeTupleList<T1, T2, T3> : List<Tuple<T1, T2, T3>>
{
    public void Add(T1 a, T2 b, T3 c)
    {
        this.Add(new Tuple<T1, T2, T3>(a, b, c));
    }

    // You can even implement a two-argument Add and mix initializers
    public void Add(T1 a, T2 b)
    {
        this.Add(new Tuple<T1, T2, T3>(a, b, default(T3)));
    }
}

このように初期化でき、3 つ、2 つ、1 つの引数の初期化子を混在させることもできます。

var mylist = new ThreeTupleList<int, string, double>()
{
    { 1, "foo", 2.3 },
    { 4, "bar", 5.6 },
    { 7, "no double here" },
    null
};
于 2012-06-24T01:49:01.693 に答える
3

あなたの問題は、それがコレクションではなく配列であるという事実から生じています。

var kvps = new KeyValuePair<string,int>[] {
    { "key1", 1 },
    { "key2", 2 }
};

本当にあるべきです:

var kvps = new KeyValuePair<string, int>[] {
    new KeyValuePair<string, int>("key1", 1),
    new KeyValuePair<string, int>("key2", 2)
};

景品はブラケットです。[]は配列です。{}コレクションです。

于 2012-06-24T01:43:00.777 に答える
2

Addメソッドが初期化構文を機能させる魔法の 秘密のソースであることを指摘してくれた複数の回答者に感謝します。したがって、問題のクラス (KeyValuePair) を継承することで目標を達成できました。

public class InitializableKVPs<T1,T2> : IEnumerable<KeyValuePair<T1,T2>>
{
    public void Add(T1 key, T2 value) 
    {
        throw new NotImplementedException();
    }

    public IEnumerator<KeyValuePair<string,string>>  GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator  IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

これはコンパイラによって受け入れられるようになりました。

var kvps = new InitializableKVPs<string,int> {
    { "key1", 1 },
    { "key2", 2 }
};

編集:Philip Daubmeierの答えには、これの実際の簡潔な実装があります。

于 2012-06-24T02:09:39.150 に答える
1

C# 6.0 構文の IDictionary をオーバーライドする

私が行ったように、誰かがここに来て、新しい C# 6.0 ディクショナリ初期化子構文のキーストロークを節約しようとして、それを行うことができますが、代わりに IDictionary から派生させる必要があります。this[] set メソッドを実装するだけでこれを機能させることができ、実装されていないメソッドが大量に残ります。

クラスの実装は次のようになります。

// Seriously!  Don't do this in production code! Ever!!!
public class CrazyAdd2 : IDictionary<string, int>
{
    public int this[string key]
    {
        get { throw new NotImplementedException(); }
        set {Console.WriteLine($"([{key}]={value})"); }
    }

#region NotImplemented
// lots of empty methods go here
#endregion
}

それを使用するには:

var crazyAdd2 = new CrazyAdd2
{
    ["one"] = 1,
    ["two"] = 2,
};

そして出力:

([one]=1)
([two]=2)

そして、ここにすべてを示すフィドルがあります:

https://dotnetfiddle.net/Lovy7m

于 2017-02-06T15:14:35.730 に答える
0

見えないかもしれませんが、同じ構文です。唯一の違いは、ディクショナリが 2 次元配列を取るのに対し、配列は入力として要素を取ることです。

int[] a = new int[]{5,6};
int[,] a = new int[]{{5,6},{3,6}};
Dictionary<int,int> a = new Dictionary<int,int>{{5,6},{3,6}};
于 2012-06-24T01:50:04.927 に答える