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

为什么可以像这样初始化Dictionary

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

…但是不要以完全相同的方式初始化KeyValuePair对象的数组:

 var kvps = new KeyValuePair[] { { "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() { "key1", 1 }等来使第二个示例工作。 但我想知道是否可以使用第一个示例中可能的相同类型的简洁语法。

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

集合初始化程序语法将转换为对Add调用,并带有适当数量的参数:

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

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

 // 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有一个Add方法,但它只有一个参数。

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

Dictionary类是专门为在内部使用KeyValuePair<,>而设计的,这是你不需要手动调用构造函数的唯一原因,而是调用双参数Add并在底层构造KeyValuePair。

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

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

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

 class ThreeTupleList : List> { public void Add(T1 a, T2 b, T3 c) { this.Add(new Tuple(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(a, b, default(T3))); } } 

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

 var mylist = new ThreeTupleList() { { 1, "foo", 2.3 }, { 4, "bar", 5.6 }, { 7, "no double here" }, null }; 

你的问题源于它是一个数组,而不是一个集合。

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

应该是:

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

赠品是括号。 []是一个数组。 {}是一个集合。

感谢多位回答者指出Add方法是神奇的 秘诀 ,使初始化语法有效。 所以我可以通过inheritance有问题的类(KeyValuePair)来实现我的目标:

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

这现在被编译器接受:

 var kvps = new InitializableKVPs { { "key1", 1 }, { "key2", 2 } }; 

编辑 :Philip Daubmeier的答案有一个实际,简洁的实现。

覆盖C#6.0语法的IDictionary

如果有人像我一样来到这里,希望为新的C#6.0字典初始化语法保存一些键击,可以完成,但需要从IDictionary派生。 你只需要实现this [] set方法就可以使它工作,这留下了大量未实现的方法。

类实现如下所示:

 // Seriously! Don't do this in production code! Ever!!! public class CrazyAdd2 : IDictionary { 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

您可能看不到它,但语法相同。 唯一的区别是数组将元素作为输入,其中字典采用二维数组。

 int[] a = new int[]{5,6}; int[,] a = new int[]{{5,6},{3,6}}; Dictionary a = new Dictionary{{5,6},{3,6}};