我目前有一个WebSocket之间的JavaScript和服务器用c#编程。在JavaScript中,我可以很容易地使用关联数组传递数据:
var data = {'test': 'val',
'test2': 'val2'};
为了在服务器端表示这个数据对象,我使用Dictionary<string, string>,但这比在JavaScript中更“打字昂贵”:
Dictionary<string, string> data = new Dictionary<string,string>();
data.Add("test", "val");
data.Add("test2", "val2");
c#中是否有一些关联数组/字典的文字符号?
使用字典字面量(c# 9提案)[被拒绝]或新语法(从c# 9开始)
c# 9引入了一个更简单的语法来创建初始化的Dictionary<TKey,TValue>对象,而无需指定Dictionary类型名称或类型参数。使用用于数组类型推断的现有规则推断字典的类型参数。
// C# 1..8
var x = new Dictionary <string,int> () { { "foo", 4 }, { "bar", 5 }};
// C# 9
var x = ["foo":4, "bar": 5];
这种语法使使用c#字典的工作更简单,并消除了冗余代码。
你可以在GitHub上跟踪这个问题(这里是c# 9的里程碑)。
编辑:该提案目前被否决:
[…我们认为关于初始化数据有很多有趣的用例,特别是像不可变字典这样的东西。我们不认为现有的字典初始化语法很繁琐,也不认为它是代码中的一种常见模式,可以从语言特性中获益良多。我们认为初始化数据的一般领域应该在我们做了记录和withers之后重新审视。[…]
当前的里程碑:
注意,从c# 9.0开始,构造函数调用表达式是目标类型的。也就是说,如果一个表达式的目标类型是已知的,你可以省略类型名,如下面的例子所示:
Dictionary<int, List<int>> lookup = new()
{
[1] = new() {1, 2, 3},
[2] = new() {5, 8, 3},
[5] = new() {1, 0, 4}
};
正如前面的例子所示,在目标类型的new表达式中总是使用圆括号。
如果new表达式的目标类型未知(例如,当使用var关键字时),则必须指定类型名称。
MSDN
你使用集合初始化器语法,但你仍然需要首先创建一个新的Dictionary<string, string>对象,因为快捷语法被翻译成一堆Add()调用(就像你的代码一样):
var data = new Dictionary<string, string>
{
{ "test", "val" },
{ "test2", "val2" }
};
在c# 6中,您现在可以选择使用更直观的Dictionary语法以及支持索引器的任何其他类型。以上语句可以改写为:
var data = new Dictionary<string, string>
{
["test"] = "val",
["test2"] = "val2"
};
与集合初始化器不同,它调用底层的索引器setter,而不是适当的Add()方法。
使用字典字面量(c# 9提案)[被拒绝]或新语法(从c# 9开始)
c# 9引入了一个更简单的语法来创建初始化的Dictionary<TKey,TValue>对象,而无需指定Dictionary类型名称或类型参数。使用用于数组类型推断的现有规则推断字典的类型参数。
// C# 1..8
var x = new Dictionary <string,int> () { { "foo", 4 }, { "bar", 5 }};
// C# 9
var x = ["foo":4, "bar": 5];
这种语法使使用c#字典的工作更简单,并消除了冗余代码。
你可以在GitHub上跟踪这个问题(这里是c# 9的里程碑)。
编辑:该提案目前被否决:
[…我们认为关于初始化数据有很多有趣的用例,特别是像不可变字典这样的东西。我们不认为现有的字典初始化语法很繁琐,也不认为它是代码中的一种常见模式,可以从语言特性中获益良多。我们认为初始化数据的一般领域应该在我们做了记录和withers之后重新审视。[…]
当前的里程碑:
注意,从c# 9.0开始,构造函数调用表达式是目标类型的。也就是说,如果一个表达式的目标类型是已知的,你可以省略类型名,如下面的例子所示:
Dictionary<int, List<int>> lookup = new()
{
[1] = new() {1, 2, 3},
[2] = new() {5, 8, 3},
[5] = new() {1, 0, 4}
};
正如前面的例子所示,在目标类型的new表达式中总是使用圆括号。
如果new表达式的目标类型未知(例如,当使用var关键字时),则必须指定类型名称。
MSDN
虽然字典初始化器的答案是完全正确的,但我要指出另一种方法(但我可能不推荐它)。如果您的目标是提供简洁的API使用,则可以使用匿名对象。
var data = new { test1 = "val", test2 = "val2"};
然后,“data”变量是“不可说的”匿名类型,因此您只能将其作为System.Object传递。然后可以编写代码将匿名对象转换为字典。这样的代码将依赖于反射,这可能会很慢。但是,您可以使用System.Reflection。Emit或System.Linq.Expressions来编译和缓存委托,这将使后续调用更快。
据我所知,Asp.net MVC api在很多地方都使用了这种技术。很多Html helper都有接受对象或字典的重载。我认为他们的API设计目标与你所追求的是一样的;方法调用时的简洁语法。
使用DynamicObject,创建一个更简单的字典初始化器并不是那么困难。
假设您想要调用以下方法
void PrintDict(IDictionary<string, object> dict) {
foreach(var kv in dict) {
Console.WriteLine (" -> " + kv.Key + " = " + kv.Value);
}
}
使用文字语法
var dict = Dict (Hello: "World", IAm: "a dictionary");
PrintDict (dict);
这可以通过创建一个这样的动态对象来实现
dynamic Dict {
get {
return new DynamicDictFactory ();
}
}
private class DynamicDictFactory : DynamicObject
{
public override bool TryInvoke (InvokeBinder binder, object[] args, out object result)
{
var res = new Dictionary<string, object> ();
var names = binder.CallInfo.ArgumentNames;
for (var i = 0; i < args.Length; i++) {
var argName = names [i];
if(string.IsNullOrEmpty(argName)) throw new ArgumentException();
res [argName] = args [i];
}
result = res;
return true;
}
}