Does C# have a short-hand syntax for creating key/value pairs?
Asked Answered
P

1

6

I have a C# method declared like so:

public void Process<K, V>(params KeyValuePair<K, V>[] items)
{
    ...
}

Usage of this method looks kind of ugly; for example:

Process(
    new KeyValuePair("key", "value"),
    new KeyValuePair(123, Guid.NewGuid())
);

In Kotlin, you can create pairs using the to infix function; for example:

val pair1 = "key" to "value"
val pair2 = 123 to UUID.randomUUID()

So the equivalent method usage looks a little tidier; for example:

process("key" to "value", 123 to UUID.randomUUID())

C# doesn't have infix functions, but something nearly equivalent could be achieved with extension methods; for example:

public static KeyValuePair<K, V> To<K, V>(this K key, V value) where K : notnull
{
    return new KeyValuePair(key, value);
}

var pair1 = "key".To("value");
var pair2 = 123.To(Guid.NewGuid());

Process("key".To("value"), 123.To(Guid.NewGuid()));

That doesn't seem like the most elegant solution, so the other thing I was considering was that C# has dictionary initializer syntax; for example:

new Dictionary<object, object>()
{
    ["key"] = "value",
    [123] = Guid.NewGuid()
}

or

new Dictionary<object, object>()
{
    { "key", "value" },
    { 123, Guid.NewGuid() }
}

So I was wondering whether dictionary initializer syntax could be applied to a method parameter; for example:

Process({ ["key"] = "value", [123] = Guid.NewGuid() });

or

Process({{ "key", "value" }, { 123, Guid.NewGuid() }});

Questions

Is dictionary initializer syntax as a method parameter possible, or is it syntactic sugar provided by the compiler when using a dictionary?

Are there any other elegant ways to create params of KeyValuePair<K, V>?

Petrol answered 18/2, 2023 at 11:33 Comment(0)
I
11

This is currently (as of 2023, C# 11) the best you can get with C# out of the box.

As you wrote yourself, C# 3 introduced shorthand init using curly brackets, C# 6 introduced named parameters init, and C# 9 allows you to use new() when the instantiated type can be inferred.

Meaning that you can do:

// C# 3+
CallMethod(new Dictionary<string, string> { { "ka", "a" }, { "kb", "b" } });

// C# 6+
CallMethod(new Dictionary<string, string> { ["ka"] = "a", ["kb"] = "b" });

// C# 9+
CallMethod(new() { { "ka", "a" }, { "kb", "b" } });

If you really want to use params, one way to reduce some keystrokes would be to use ValueTuples, something like

public static Dictionary<K, V> CallMethod<K, V>(params (K key, V value)[] values)
{
    var dict = new Dictionary<K, V>();
    foreach (var kvp in values)
    {
        dict[kvp.key] = kvp.value;
    }
    return dict;
}

and then you can do

// C# 7+, return type is inferred to be Dictionary<string, string>
CallMethod(("ka", "a"), ("kb", "b"));

[Update]

There is also a proposed update for collection literals in C#12 C#13 which would allow you to use the following syntax:

// C# 13+ proposal (originally C# 12, but moved)
CallMethod(["ka": "a", "kb": "b"]);
Igraine answered 18/2, 2023 at 12:8 Comment(1)
The dictionary part of the C#12 collection expressions proposal was moved to a C#13 proposal: github.com/dotnet/csharplang/issues/7822Vshaped

© 2022 - 2025 — McMap. All rights reserved.