233

I currently have a WebSocket between JavaScript and a server programmed in C#. In JavaScript, I can pass data easily using an associative array:

var data = {'test': 'val',
            'test2': 'val2'};

To represent this data object on the server side, I use a Dictionary<string, string>, but this is more 'typing-expensive' than in JavaScript:

Dictionary<string, string> data = new Dictionary<string,string>();
data.Add("test", "val");
data.Add("test2", "val2");

Is there some kind of literal notation for associative arrays / Dictionarys in C#?

pimvdb
  • 151,816
  • 78
  • 307
  • 352
  • C#9 contains a proposal for Dictionary Literals. I post an [answer about it below](https://stackoverflow.com/a/56395631/1248177). Our dream come true! – aloisdg May 31 '19 at 13:03
  • Note that the C#9 Dictionary Literals syntax in the preceding comment was just a proposal, which has been rejected. – Jan Dec 28 '22 at 05:13

4 Answers4

361

You use the collection initializer syntax, but you still need to make a new Dictionary<string, string> object first as the shortcut syntax is translated to a bunch of Add() calls (like your code):

var data = new Dictionary<string, string>
{
    { "test", "val" }, 
    { "test2", "val2" }
};

In C# 6, you now have the option of using a more intuitive syntax with Dictionary as well as any other type that supports indexers. The above statement can be rewritten as:

var data = new Dictionary<string, string>
{
    ["test"] = "val",
    ["test2"] = "val2"
};

Unlike collection initializers, this invokes the indexer setter under the hood, rather than an appropriate Add() method.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 11
    Note that it's not a literal notation, strictly speaking... it's just a shortcut for initialization. Only string and some primitive types have a literal representation – Thomas Levesque Feb 12 '11 at 20:47
  • If you mean you want to remove one of the "string"s - they are not redundant - one is for the key type, the other for the value type. There is no literal specific for Dictionaries, the notation used in this answer is general for all classes with an Add method that takes two strings (and implements IEnumerable). – Markus Johnsson Feb 12 '11 at 20:50
  • 3
    @Markus Johnsson: He meant, literally, `Dictionary`. My code originally declared the type, I had just changed it to `var` after his comment. – BoltClock Feb 12 '11 at 20:51
  • 3
    Warning regarding the c# 6 syntax - duplicate key entries will *not* throw any exception. Instead, the "latest" key/value pair will always take precedence. – Brad M Sep 19 '19 at 15:15
18

While, the dictionary initializer answer is totally correct, there is another approach to this that I would point out (but I might not recommend it). If your goal is to provide terse API usage, you could use anonymous objects.

var data = new { test1 = "val", test2 = "val2"};

The "data" variable is then of an "unspeakable" anonymous type, so you could only pass this around as System.Object. You could then write code that can transform an anonymous object into a dictionary. Such code would rely on reflection, which would potentially be slow. However, you could use System.Reflection.Emit, or System.Linq.Expressions to compile and cache a delegate that would make subsequent calls much faster.

Asp.net MVC APIs use this technique in a number of places that I've seen. A lot of the Html Helpers have overloads that accept either an object or a dictionary. I assume the goal of their API design is the same as what you are after; terse syntax at the method call.

MarkPflug
  • 28,292
  • 8
  • 46
  • 54
  • 1
    That's only going to help when the set of keys is static. You can also use Tuples for the same purpose. – sehe Jul 07 '18 at 23:23
17

Use Dictionary Literals (C#9 proposal) [rejected] or the new syntax (beginning with C#9)

[this may happens but is currently rejected]

C#9 introduces a simpler syntax to create initialized Dictionary<TKey,TValue> objects without having to specify either the Dictionary type name or the type parameters. The type parameters for the dictionary are inferred using the existing rules used for array type inference.

// C# 1..8    
var x = new Dictionary <string,int> () { { "foo", 4 }, { "bar", 5 }};   
// C# 9    
var x = ["foo":4, "bar": 5];  

This synthax makes the work with dictionaries in C# simpler and removing the redundant code.

You can follow the issue on GitHub (and here is the milestone for C#9).

Edit: This proposal is currently rejected:

[...] We think there are a number of interesting use cases around initializing data, particularly for things like immutable dictionaries. We don't find the existing syntax for initializing a dictionary that onerous, nor do we see it as a frequent pattern in code that would benefit much from a language feature. We thing that the general area of initializing data should be looked at again after we do records and withers. [...]

current milestone:

likely never

Note that beginning with C# 9.0, constructor invocation expressions are target-typed. That is, if a target type of an expression is known, you can omit a type name, as the following example shows:

Dictionary<int, List<int>> lookup = new()
{
    [1] = new() {1, 2, 3},
    [2] = new() {5, 8, 3},
    [5] = new() {1, 0, 4}
};

As the preceding example shows, you always use parentheses in a target-typed new expression.

If a target type of a new expression is unknown (for example, when you use the var keyword), you must specify a type name.

MSDN

aloisdg
  • 22,270
  • 6
  • 85
  • 105
  • 3
    Great idea, hope it makes it in, seems like a no brainer – jjxtra Sep 16 '19 at 21:15
  • It costs me too much time to reach "Edit: rejected", please edit the post, don't make future readers waste their time. The starting sentence "C#9 introduces ..." is a lie. – Sinatr Jul 10 '23 at 11:09
  • @Sinatr well it is the first line – aloisdg Jul 10 '23 at 20:50
  • Perhaps it's just my personal "blindness" or the way how I read posts on SO, but I begin reading from "C#9 introduces", ignoring the first bold sentence completely. You have twice "rejected" written in very obscure manner. Could you make it MORE prominent? Like MUCH more? Thanks! Your answer will be great, but only when feature is implemented. Currently it is rather harmful, than useful. I've wasted several minutes by trying to use json-like syntax to initialize dictionaries and trying to understand why it doesn't work. My fault sure, wouldn't happen without your post here ;) – Sinatr Jul 11 '23 at 07:35
  • 1
    @Sinatr I added a third line – aloisdg Jul 11 '23 at 20:55
9

Using DynamicObject, it is not that difficult to create a simpler dictionary initializer.

Imagine you want to call the following method

void PrintDict(IDictionary<string, object> dict) {
    foreach(var kv in dict) {
        Console.WriteLine ("  -> " + kv.Key + " = " + kv.Value);
    }
}

using a literal syntax like

var dict = Dict (Hello: "World", IAm: "a dictionary");
PrintDict (dict);

This can be accomplished by creating a dynamic object like this

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;
    }
}
Krumelur
  • 31,081
  • 7
  • 77
  • 119