HomeSearch

C# KeyValuePair Examples

Loop over a Dictionary with foreach and KeyValuePair. Understand read-only errors.
KeyValuePair. The crow pushes against the latch. The gate unlocks. Inside, it finds seed and nourishment. The action (a push with its beak) has a result (a value).
For the crow, the action and the value are not just separate. They are linked together. In C# a KeyValuePair struct (often used inside dictionaries) joins 2 things together.
First example. KeyValuePair is not just for the birds. Here we use it in a List. We store pairs of values in a single List. Two Lists could be used, but that might complicate matters.

Here: We initialize a new List of type KeyValuePair. This shows the required syntax form.

Note: Inside the brackets in the KeyValuePair, there are 2 types separated by a comma (string, int).

Constructor: We can create a new KeyValuePair with its constructor. The constructor is shown in the Add() calls.

C# program that uses KeyValuePair using System; using System.Collections.Generic; class Program { static void Main() { // Shows a List of KeyValuePairs. var list = new List<KeyValuePair<string, int>>(); list.Add(new KeyValuePair<string, int>("Cat", 1)); list.Add(new KeyValuePair<string, int>("Dog", 2)); list.Add(new KeyValuePair<string, int>("Rabbit", 4)); foreach (var element in list) { Console.WriteLine(element); } } } Output [Cat, 1] [Dog, 2] [Rabbit, 4]
Return, KeyValuePair. Often we need to return 2 values from a method. We can do this with KeyValuePair. Here is an example of the syntax—it can be tricky at first to use.

Tip: This is clearer than a 2-element array. Consider out or ref parameters instead.

Parameters
C# program that returns two values using System; using System.Collections.Generic; class Program { static void Main() { Console.WriteLine(GetNames()); } static KeyValuePair<string, string> GetNames() { // Gets collection of first and last name. string firstName = "Sundar"; string lastName = "Pichai"; return new KeyValuePair<string, string>(firstName, lastName); } } Output [Sundar, Pichai]
Read-only error. When using KeyValuePair, you may get this error. The C# compiler doesn't allow you to assign the Key and Value properties. This must be assigned in the constructor.Readonly
C# program that causes read-only error using System.Collections.Generic; class Program { static void Main() { var pair = new KeyValuePair<string, int>("bird", 10); pair.Key = "rat"; } } Output Property or indexer 'System.Collections.Generic.KeyValuePair...Key' cannot be assigned to--it is read-only.
Foreach, Dictionary. A common use of KeyValuePair is in a loop over a Dictionary. The Dictionary has an enumerator that returns each key and value in a KeyValuePair, one at a time.Dictionary

Var: An improved syntax could be to use the var keyword with the foreach loop over your Dictionary. This shortens the syntax.

Var
C# program that uses foreach loop on Dictionary using System; using System.Collections.Generic; class Program { static void Main() { Dictionary<string, int> birds = new Dictionary<string, int>() { { "crow", 10 }, { "robin", 5 } }; // Use KeyValuePair to use foreach on Dictionary. foreach (KeyValuePair<string, int> bird in birds) { Console.WriteLine($"Pair here: {bird.Key}, {bird.Value}"); } } } Output Pair here: crow, 10 Pair here: robin, 5
Sort, lambda. To sort KeyValuePairs, we can use Comparison methods. A lambda is the cleanest way to do this. Here we sort in descending order by the Value of each pair.

Tip: For descending, we take the second argument, and compare it to the first (instead of the other way around).

C# program that uses lambda, sorts KeyValuePairs using System.Collections.Generic; class Program { static void Main() { var data = new List<KeyValuePair<int, int>>() { new KeyValuePair<int, int>(1, 6), new KeyValuePair<int, int>(1, 2), new KeyValuePair<int, int>(3, 4) }; // Sort pairs in list in descending order based on the value. // ... Use reverse order of A and B to mean descending sort. data.Sort((a, b) => (b.Value.CompareTo(a.Value))); foreach (var pair in data) { System.Console.WriteLine(pair); } } } Output [1, 6] [3, 4] [1, 2]
Implementation. Here is the basic layout of the KeyValuePair struct. The KeyValuePair has two private fields, and two public properties that retrieve the values of those fields.Property
Implementation of KeyValuePair: C# [Serializable, StructLayout(LayoutKind.Sequential)] public struct KeyValuePair<TKey, TValue> { private TKey key; private TValue value; public KeyValuePair(TKey key, TValue value); public TKey Key { get; } public TValue Value { get; } public override string ToString(); }
ToString. When you want to display the values, call ToString or pass the KeyValuePair to Console.Write or Console.WriteLine. This will implicitly call ToString.Console.Write

Tip: Internally ToString uses a StringBuilder. This may cause memory pressure. Avoiding ToString can speed up programs.

StringBuilder

Program: We invoke ToString() on the KeyValuePair, and also convert that string to an uppercase string with ToUpper.

C# program that uses ToString using System; using System.Collections.Generic; class Program { static void Main() { var pair = new KeyValuePair<string, int>("bird", 10); // Get string from KeyValuePair. string result = pair.ToString(); Console.WriteLine("TOSTRING: {0}", result); Console.WriteLine("UPPER: {0}", result.ToUpper()); } } Output TOSTRING: [bird, 10] UPPER: [BIRD, 10]
Benchmark, memory use. KeyValuePair wastes no memory. It is compact. Consider this example program: it has 2 versions of code, each of which store keys and values.

Version 1: The keys and values are stored in 2 separate arrays. The GC.GetTotalMemory method measures the memory usage.

ArrayGC.Collect

Version 2: This version uses a single array of KeyValuePair structs. Again, the GC.GetTotalMemory method is used.

Result: The version that uses 1 array of KeyValuePairs uses a few bytes less of memory. So KeyValuePair wastes no space.

Tip: Performance may be affected by the KeyValuePair version, as we must access properties rather than array elements.

C# program that tests memory usage for KeyValuePair using System; using System.Collections.Generic; class Program { static void Main() { const int size = 10000; Console.WriteLine("::2 ARRAYS::"); TestArrays(size); Console.WriteLine("::1 KEYVALUEPAIR ARRAY::"); TestKeyValuePairs(size); } static void TestArrays(int size) { long mem1 = GC.GetTotalMemory(false); string[] keys = new string[size]; int[] values = new int[size]; keys[0] = "bird"; values[1] = 10; long mem2 = GC.GetTotalMemory(false); // Collect garbage. GC.Collect(); Console.WriteLine(mem2 - mem1); keys[0] = ""; } static void TestKeyValuePairs(int size) { long mem1 = GC.GetTotalMemory(false); KeyValuePair<string, int>[] array = new KeyValuePair<string, int>[size]; array[0] = new KeyValuePair<string, int>("bird", 10); long mem2 = GC.GetTotalMemory(false); // Collect garbage. GC.Collect(); Console.WriteLine(mem2 - mem1); array[0] = new KeyValuePair<string, int>("", 0); } } Output ::2 ARRAYS:: 80048 ::1 KEYVALUEPAIR ARRAY:: 80024
Benchmark, structs. Is there any advantage to using custom structs instead of KeyValuePair generic types? The 2 approaches are equivalent in functionality.

Next: We look at a benchmark that compares 2 structs. It seems logical the methods should have the same performance.

But: I found the methods are inlined in different ways. Something small differences matter for performance.

Version 1: This code calls a method that receives a CustomPair struct instance.

Struct

Version 2: This code calls a method overload that receives a KeyValuePair struct (method overloading selects the correct method).

Overload
C# program that tests KeyValuePair performance using System; using System.Collections.Generic; using System.Diagnostics; struct CustomPair { public int Key; public string Value; } class Program { const int _max = 300000000; static void Main() { CustomPair p1; p1.Key = 4; p1.Value = "perls"; Method(p1); KeyValuePair<int, string> p2 = new KeyValuePair<int, string>(4, "perls"); Method(p2); for (int a = 0; a < 5; a++) { var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { Method(p1); Method(p1); } s1.Stop(); var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { Method(p2); Method(p2); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } Console.Read(); } static int Method(CustomPair pair) { return pair.Key + pair.Value.Length; } static int Method(KeyValuePair<int, string> pair) { return pair.Key + pair.Value.Length; } } Output 0.32 ns CustomPair 4.35 ns KeyValuePair 0.32 ns 4.34 ns 0.32 ns 4.36 ns 0.32 ns 4.35 ns 0.32 ns 4.36 ns
Performance, analysis. To analyze the results, I looked inside the 2 Method implementations in the IL Disassembler tool. They have the same code size.

But: In the KeyValuePair version, the call instruction is used instead of ldfld because KeyValuePair uses properties.

IL Disassembler

Tip: After C# compilation, the program is JIT-compiled during runtime. The behavior of the inliner is sometimes hard to determine.

Thus: It is possible to improve performance by replacing a KeyValuePair with a regular struct.

Benchmark
Sort, no lambdas. How can we sort a collection of KeyValuePair instances? We can implement custom sorting Comparison method—no lambdas are required. We use the delegate method syntax.Sort KeyValuePair List
Sort, parallel lists. We can use KeyValuePair in a List to create 2 parallel Lists. These are easily sorted, keeping both values together.

Shuffle: Randomly shuffled arrays are more complicated than you might at first expect. KeyValuePair, and List, can help here.

Shuffle Array
Discussion. In some contexts—such as internal method code—using KeyValuePair is convenient and simple. But using a class can enhance the clarity of your program.Class

Therefore: Prefer classes when the usage is not trivial. This improves object-oriented design.

Tuple. Another option in the .NET Framework is the Tuple type. You can have a 2-element Tuple. A Tuple is a class, not a struct. It can also have many more items in it.Tuple
A summary. Keys and values are everywhere—if you think carefully. A term has a definition. An action has a result. KeyValuePair has many uses throughout C# programs.
© 2007-2019 Sam Allen. Every person is special and unique. Send bug reports to info@dotnetperls.com.
Home
Dot Net Perls