104

I've generally used the KeyValuePair<TKey,TValue> type whenever I have data that is pair-related in the sense that one is a key to the other. If the data is unrelated then the Tuple<T1,T2> type makes more sense and I'd go with that.

Now I just read this article about why to generally avoid KeyValuePair<TKey,TValue> and prefer Tuple<T1,T2>. The primary argument being the performance benefit of Tuple<T1,T2>.

Outside performance, is there any reason a KVP would be a better choice than a Tuple<T1,T2>?

Nick Gotch
  • 9,167
  • 14
  • 70
  • 97
  • 5
    A `KeyValuePair` is a key and a value, a `Tuple` is just a pair of _equal_ values. You could also ask: "why should i use a `List` if i can use `Dictionary`". – Tim Schmelter Oct 22 '13 at 15:44
  • 4
    Right, but in that case you can use the key to look up data. It means something. In this case, the names are just semantics, they don't mean anything (to the processor.) – Nick Gotch Oct 22 '13 at 15:49
  • 1
    A tuple isn't a pair of equal values, but some number of equal types. Maybe this is seen as nitpicking, but e.g. C does have the union construct for different representations of equal values. :) – Jonas Oct 01 '15 at 14:30

3 Answers3

80

Well, the type could be considered poorly named, for one. A KeyValuePair as named should represent a key and a value. What if your two objects aren't really a key and a value, just two things? If I were to see a method or property that had a type of KeyValuePair<TKey, TValue>, I would expect the values of the KVP to be a key and a value. This is really just a matter of communicating intention and making it clear to yourself in the future, or possibly other team members. A tuple does not indicate that kind of association.

Tuples also make it easier to add another value, making it a 3-tuple (or a triplet, however you want to call it). Some .NET languages, like F#, have special syntax around tuples as well.

For an implementation perspective, Tuple does many things KeyValuePair does not. Tuples are comparable, they implement the IComparable and IStructuralEquatable interfaces, so it makes it easier to compare two tuples.

vcsjones
  • 138,677
  • 31
  • 291
  • 286
  • 1
    I found out the hard way that you can put KeyValuePairs into a dictionary but never will get a result back then. – MKesper Jul 06 '16 at 06:54
  • 3
    In addition, the new C# 7.0 supports new, simpler syntax for Tuples, making them much easier *and* performant to work with than KeyValuePairs. https://visualstudiomagazine.com/articles/2017/01/01/tuples-csharp-7.aspx – Jacob Stamm Jan 20 '17 at 04:49
  • 1
    Also, the ability to name the parameters in tuples make it easier for the consumer to understand what they're supposed to be used for. In a generic like KVP, it's really anyone's guess -- unless it's specifically documented somewhere -- what the key is supposed to "be" -- i.e. not its type but what real-world thing it is, like a setting name, a social security number, etc. – rory.ap Dec 21 '18 at 15:01
45

KeyValuePair is struct and Tuple is a class.

That is the main difference which influences how the objects are copied whether by reference or values.

and hence Tuple<T1,T2> when passed around just uses "4byte" in 32bit OS, whereas KeyValuePair<K,V> requires more based on "K and V"

Anyhow comparing Tuple and KeyValuePair is not a good idea(doesn't makes sense for me) since both serves different purpose.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • 4
    How they serve a different purpose? would you please elaborate on it. – OldSchool Sep 18 '17 at 16:26
  • 2
    @YakRangi keyvaluepair is meant to be used as a container for keys and values in a dictionary otherwise, it serves no purpose. On the other hand Tuple can be used to store any arbitrarily related members together. Also with Tuple you can store multiple members combined not just 2. – Sriram Sakthivel Sep 18 '17 at 19:04
  • 1
    @SriramSakthivel This means, the answer to the OP question is: don't use KVP, unless you are browsing a Dictionary. – Alex Fainshtein May 02 '19 at 19:54
34

Despite the semantics, performance may be an important consideration as you consider both options. As previously mentioned, the KeyValuePair is a value type (struct), whereas the Tuple<> is a reference type (class). Therefore, the KeyValuePair is allocated on the stack and the Tuple<> is allocated on the heap, and the optimal choice is usually determined by the classic arguments of Stack vs. Heap Memory Allocation. In short, stack space is limited, but generally has very fast access. The heap memory is much larger but is somewhat slower.

KeyValuePair<T1, T2> may be the better choice if both the key and value types are primitives (value types like int, bool, double, etc.) or structs of small size. With primitive types on the stack, allocation and deallocation is lightning fast. This can really affect performance, especially as arguments to recursive method calls.

On the other hand, Tuple<T1, T2> is likely the better choice if either T1 or T2 are reference types (like classes). A KeyValuePair that contains pointers to reference types (as the key or value types) sort of defeats the purpose since the objects will need to be looked up on the heap anyway.

Here's a benchmark I found online: Tuple vs. KeyValuePair. The only problem with this benchmark is that they tested KeyValuePair<string, string> vs. Tuple<string, string>, and the string type is an unusual and special type in .NET in that it can behave both like a value type and/or a reference type depending on the execution context. I believe the KeyValuePair<int, int> would have been a clear winner against Tuple<int, int>. Even with the deficiencies, however, the results show that the performance differences can be significant:

8.23 ns -- Allocate Tuple
0.32 ns -- Allocate KeyValuePair (25x faster!)

1.93 ns -- Pass Tuple as argument
2.57 ns -- Pass KeyValuePair as argument

1.91 ns -- Return Tuple
6.09 ns -- Return KeyValuePair

2.79 ns -- Load Tuple from List
4.18 ns -- Load KeyValuePair from List

Special Sauce
  • 5,338
  • 2
  • 27
  • 31
  • Just as a point, this is missing a very important test: Access a struct directly from an array. That is direct memory access and does not return a copy of the struct, unlike List. – Alkis Tsapanidis Jun 07 '23 at 13:22