我目前有一个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;
    }
}