如何使内联数组初始化工作,如字典初始化?

McG*_*gle 21 c# syntax initialization magic-methods

为什么可以这样初始化Dictionary<T1,T2>:

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

...但是不要以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."
Run Code Online (Sandbox Code Playgroud)

我意识到我可以通过new KeyValuePair<string,int>() { "key1", 1 }为每个项目编写等来使第二个例子工作.但我想知道是否可以使用第一个示例中可能的相同类型的简洁语法.

如果不可能,那么是什么让Dictionary类型如此特别?

Mar*_*ers 33

集合初始化程序语法被转换为Add具有适当数量的参数的调用:

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

这种特殊的初始化语法也适用于具有Add方法和实现的其他类IEnumerable.让我们创建一个完全疯狂的类,只是为了证明没有什么特别之处,Dictionary并且这个语法可以适用于任何合适的类:

// 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(); }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以这样写:

var crazyAdd = new CrazyAdd
{
    {1, 2, 3},
    {4, 5, 6}
};
Run Code Online (Sandbox Code Playgroud)

输出:

6
15
Run Code Online (Sandbox Code Playgroud)

看到它在线工作:ideone

至于您询问的其他类型:

  • 它不适用于数组,因为它没有Add方法.
  • List<T>有一个Add方法,但它只有一个参数.


Phi*_*ier 11

它确实适用于Dictionary,因为它有一个带有Add两个参数的重载.数组甚至没有Add方法,更不用说有两个参数.

Dictionary班是专门设计用于与工作KeyValuePair<,>在内部,就是你不要在构造函数中需要调用的唯一原因,而不是两个参数Add被调用,并在引擎盖下构建KeyValuePair.

其他每个IEnumerable<KeyValuePair<,>>都没有这个特殊的实现,因此必须以这种方式初始化:

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

您可以使用自己的类创建相同的行为,比如说实现这个:

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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样初始化它,甚至混合三个,两个和一个参数的初始化器:

var mylist = new ThreeTupleList<int, string, double>()
{
    { 1, "foo", 2.3 },
    { 4, "bar", 5.6 },
    { 7, "no double here" },
    null
};
Run Code Online (Sandbox Code Playgroud)