C-Sharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript | SQL | PHP | Angular | HTML
The KeyValuePair type in System.Collections.Generic is simple and always available. It is used internally in Dictionary.
Example. First, this example uses KeyValuePair in a List, which is also in System.Collections.Generic. This is useful for storing pairs of values in a single List. You could use two separate Lists, but that can 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 two types separated by a comma (string, int).
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]
Also, we can create a new KeyValuePair with its constructor. The constructor is shown in the List.Add calls. The KeyValuePair's constructor returns the new KeyValuePair, and that instance is added.
Note: Instead of a List, you could use an array here. You can specify the KeyValuePair<string, int> as the type of the array.
Example 2. Often, you need to return two separate values from a method. You can do this easily with KeyValuePair. You must specify the exact type in the return value, and then return the new KeyValuePair in the method body.
Tip: This is clearer than a two-element array. Consider out or ref parameters instead.
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 = "William"; string lastName = "Gates"; return new KeyValuePair<string, string>(firstName, lastName); } } Output [William, Gates]
Error. When using KeyValuePair in your program, you will likely get this error at some point. The C# compiler doesn't allow you to assign the Key and Value properties. This must be assigned in the constructor.
Error: Property or indexer 'System.Collections.Generic.KeyValuePair...Key' cannot be assigned to--it is read-only.
Dictionary loop. Probably the most popular usage of KeyValuePair is in a loop over a Dictionary. The Dictionary collection in C# has an enumerator that returns each key and value in a KeyValuePair, one at a time. Examples are available.
Also: An improved syntax could be to use the var keyword with the foreach loop over your Dictionary. This shortens the syntax.
Sort. How can you sort a collection of KeyValuePair instances? You can implement a custom sorting Comparison method. We use the delegate method syntax. The linked tutorial contains information on this approach.
Also, you may use KeyValuePair in a List to create two parallel Lists. These are easily sorted, keeping both values together. This site has an example of an accurate shuffle algorithm with KeyValuePair and List.
Implementation. You should know the basic layout of the KeyValuePair struct. Here, we see the internal code. The KeyValuePair has two private fields, and two public properties that retrieve the values of those fields.
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. The ToString method is useful. When you want to display the values, simply call ToString or pass the KeyValuePair to Console.Write or Console.WriteLine. This will implicitly call ToString. Internally, ToString uses a StringBuilder.
Performance. Is there any advantage to using custom structs instead of KeyValuePair generic types? Conceptually, the two approaches should be precisely equivalent in functionality, but there are some differences in performance.
KeyValuePair performance KeyValuePair influenced how the method was inlined. Method that uses normal struct: 0.32 ns Method that uses KeyValuePair: 4.35 ns
Next, we figure out what we are comparing. It is always possible to use custom structs with two fields instead of a KeyValuePair with those types. My question was whether this is ever worthwhile doing.
Version 1 struct CustomPair { public int Key; public string Value; } Version 2 KeyValuePair<int, string>
Next, we look at a benchmark that compares the two structs. You would think that the .NET Framework would compile the two methods in the exactly same way, but I found the methods are inlined in different ways.
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 = "deves"; Method(p1); KeyValuePair<int, string> p2 = new KeyValuePair<int, string>(4, "deves"); 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; } } Result 0.32 ns 4.35 ns 0.32 ns 4.34 ns 0.32 ns 4.36 ns 0.32 ns 4.35 ns 0.32 ns 4.36 ns
I looked inside the two 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.
After C# compilation, the program is JIT-compiled during runtime. The behavior of the inliner is sometimes hard to determine. Extra members that need inlining sometimes influence the inliner and end up reducing performance.
Tip: It is possible to improve performance by replacing a KeyValuePair with a regular struct.
Discussion. In some contexts—such as internal method code—using KeyValuePair is convenient and simple. But using a class or struct you define yourself can definitely enhance the object-orientation of your program.
Therefore: I suggest you prefer classes when the usage is not trivial. This improves object-oriented design.
ClassObject-Oriented Programming
Tuple. Another option now available in the .NET Framework is the Tuple type. You can have a two-element Tuple. A Tuple is a class, not a struct. It can also have many more items in it.
Summary. We saw examples of using KeyValuePair in the C# language, and also looked into its internals in the .NET Framework. Lists and Dictionaries are ideal companions for KeyValuePairs. We returned the collection from methods.