为什么可以像这样初始化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 }
等来使第二个示例工作。但我想知道是否可以使用第一个示例中可能的相同类型的简洁语法。
如果不可能,那么 Dictionary 类型的特殊之处是什么?
答案 0 :(得分:33)
将集合初始值设定项语法转换为对Add
的调用,并使用适当数量的参数:
var dict = new Dictionary<string,int>();
dict.Add("key1", 1);
dict.Add("key2", 2);
这种特殊的初始化语法也适用于具有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(); }
}
现在你可以这样写:
var crazyAdd = new CrazyAdd
{
{1, 2, 3},
{4, 5, 6}
};
输出:
6
15
查看在线工作:ideone
至于您询问的其他类型:
Add
方法。List<T>
有一个Add
方法,但它只有一个参数。答案 1 :(得分: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)
};
您可以使用自己的类创建相同的行为,比如说实现这个:
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)));
}
}
你可以像这样初始化它,甚至混合三个,两个和一个参数的初始化器:
var mylist = new ThreeTupleList<int, string, double>()
{
{ 1, "foo", 2.3 },
{ 4, "bar", 5.6 },
{ 7, "no double here" },
null
};
答案 2 :(得分: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)
};
赠品是括号。 []
是一个数组。 {}
是一个集合。
答案 3 :(得分:2)
感谢多位回答者指出添加方法是神奇的东西 秘密酱,它使初始化语法有效。所以我可以通过继承有问题的类(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的答案有一个实际,简洁的实现。
答案 4 :(得分:1)
如果有人来到这里,就像我一样,希望为新的C#6.0字典初始化程序语法保存一些按键,可以完成,但需要从IDictionary派生。你只需要实现this [] set方法就可以使它工作,这留下了大量未实现的方法。
类实现如下所示:
webview
然后使用它:
// 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,
};
这是一个演示整个事情的小提琴:
答案 5 :(得分:0)
您可能看不到它,但语法相同。唯一的区别是数组将元素作为输入,其中字典采用二维数组。
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}};