如何使内联数组初始化工作,如字典初始化?
为什么可以像这样初始化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)
这是一个展示整个事物的小提琴:
您可能看不到它,但语法相同。 唯一的区别是数组将元素作为输入,其中字典采用二维数组。
int[] a = new int[]{5,6}; int[,] a = new int[]{{5,6},{3,6}}; Dictionary a = new Dictionary{{5,6},{3,6}};