93

I've seen in (amongst others) this question that people wonder how to initialize an instance of KeyValuePair, which expectedly should look like this.

KeyValuePair<int, int> keyValuePair = new KeyValuePair<int, int>
{ 
  Key = 1,
  Value = 2
};

It doesn't work, as if the properties aren't there. Intead, I need to use the constructor like this.

KeyValuePair<int, int> keyValuePair = new KeyValuePair<int, int>(1, 2);

Admittedly shorter syntax but it bothers me that I can't use the initializer. What am I doing wrong?

Community
  • 1
  • 1

9 Answers9

96

You are not wrong you have to initialise a keyValuePair using

KeyValuePair<int, int> keyValuePair = new KeyValuePair<int, int>(1, 2);

The reason that you cannot use the object initialisation syntax ie { Key = 1, Value = 2 } is because the Key and Value properties have no setters only getters (they are readonly). So you cannot even do:

keyValuePair.Value = 1; // not allowed
Kevin Holditch
  • 5,165
  • 3
  • 19
  • 35
24

Dictionaries have compact initializers:

var imgFormats = new Dictionary<string, ChartImageFormat>()
{
    {".bmp", ChartImageFormat.Bmp}, 
    {".gif", ChartImageFormat.Gif}, 
    {".jpg", ChartImageFormat.Jpeg}, 
    {".jpeg", ChartImageFormat.Jpeg}, 
    {".png", ChartImageFormat.Png}, 
    {".tiff", ChartImageFormat.Tiff}, 
};

In this case the dictionary i used to associate file extensions with image format constants of chart objects.

A single keyvaluepair can be returned from the dictionary like this:

var pair = imgFormats.First(p => p.Key == ".jpg");
flodis
  • 1,123
  • 13
  • 9
16

KeyValuePair<int, int> is a struct, and, fortunately, it is immutable struct. In particular, this means that its properties are read only. So, you can't use object intializer for them.

Dennis
  • 37,026
  • 10
  • 82
  • 150
  • +1 the 'struct' point is what explains it all. As you say, read only properties are just a consequence of KVP being a struct – jbl Mar 19 '13 at 09:14
  • @dennis it's an immutable struct because it's properties are read-only, not the other way round. – Phil Mar 19 '13 at 09:18
  • 1
    @Phil: I know this, thanks. The point is that KVP is a well-designed struct, because it is immutable. Of course, `struct` itself doesn't equal `immutability`. – Dennis Mar 19 '13 at 09:24
  • @Phil of course, but this often leads to errors (like trying to update a property in a method just by passing the value and not the reference) Hence the good practice of making the properties read-only – jbl Mar 19 '13 at 09:25
  • Of course, this is exactly the same for an immutbale ***class***, for example the code `new Tuple { Item1 = 1, Item2 = 2 }` gives the exact same error (together with an error that no constructor overload for `Tuple<,>` takes 0 arguments). – Jeppe Stig Nielsen Mar 19 '13 at 09:52
  • What is the advantage of that design? I'm assuming it's an intentional behavior. –  Mar 19 '13 at 10:27
  • @CRMconfusee Immutable **objects** are for code safety reasons. Passing an object by value is passing the object's *reference* by value. In other words, you can modify the object in the original thread after you pass it and affect the passed object. – Powerlord Jun 05 '15 at 20:09
15

Ok you have the answers. As an alternative, I prefer factory pattern similar to Tuple class, for type inference magic :)

public static class KeyValuePair
{
    public static KeyValuePair<K, V> Create<K, V>(K key, V value)
    {
        return new KeyValuePair<K, V>(key, value);
    }
}

So short becomes shorter:

var keyValuePair = KeyValuePair.Create(1, 2);
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • 1
    Since 2017, this static method `Create` has been a part of .NET (but not .NET Framework), so you do not have to write the method yourself anymore. – Jeppe Stig Nielsen Mar 15 '23 at 14:50
8

Here goes an example that does the job

KeyValuePair<int, int> kvp = new KeyValuePair<int, int>(1, 1);
Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
5

The Key and Value properties have no setters. Thats why you can't use them in the initializer. Just use the constructor :) and you'll be fine.

Mihail Shishkov
  • 14,129
  • 7
  • 48
  • 59
3

I also prefer factory pattern. But I found this way much more to be more useful when I had to create a pair outside. This way I can support any simple to complex use case.

This way I could use any Type and make KeyValue Pairs from its properties or any predicate I want, But cleaner. Like the way similar to IEnumerable.ToDictionary(keySelector,valueSelector)


        public static KeyValuePair<TKey, TValue> CreatePair<TSource, TKey, TValue>(
         this TSource source,
         Func<TSource, TKey> keySelector, 
         Func<TSource, TValue> valueSelector)
        {
            if (source is null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (keySelector is null)
            {
                throw new ArgumentNullException(nameof(keySelector));
            }

            if (valueSelector is null)
            {
                throw new ArgumentNullException(nameof(valueSelector));
            }

            return new KeyValuePair<TKey, TValue>(
                         keySelector.Invoke(source), 
                         valueSelector.Invoke(source));
        }

And you use.

yourObject.CreatePair(
        x=> x.yourKeyPropery, 
        x=> SomeOperationOnYourProperty(x.yourValueProperty));
Jins Peter
  • 2,368
  • 1
  • 18
  • 37
1

The Key and Value properties are read-only, therefore you can't use them in an object initializer.

See this entry in the C# programming guide.

Phil
  • 42,255
  • 9
  • 100
  • 100
1

You aren't doing something wrong. The KeyValuePairs properties are read-only. You cannot set them. Additionally, there is no empty default constructor. You need to use the constructor provided.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • 1
    It is correct that there is no default constructor. However the code `var test = new KeyValuePair();` will still be OK. The reason is that `KeyValuePair<,>` is a struct. And for a struct, the default value (also called `default(KeyValuePair)`) will be created if you use a `new` object expression with zero arguments. In this case, a pair where both key and value are `default(int)` which is zero. – Jeppe Stig Nielsen Mar 19 '13 at 10:01
  • I believe that's incorrect. My IntelliSense tells me that I can create *new KeyValuePair()*, whatever that might be good for. Aren't all classes supposed to have a default empty constructor? Or was it in Java...? –  Mar 19 '13 at 10:26
  • @CRMconfusee no thats not a requirement for classes in C#, but for structs. – nawfal Jun 12 '13 at 20:42