Using tuples to pass simple data quickly in C#

Did you know that a method can return multiple values in C#? That’s thanks to tuples and deconstruction!

This article is also available on Medium.

Starting from C# 7.0 (released with Visual Studio 2017), the language has a great feature that I’ve only recently really dived into: tuples.

Tuples allow you to group together loosely related data to create temporary (but mutable) lightweight data structures that can be passed around to or from methods easily and overall make your code more readable.

They are pretty straight-forward to implement and they can be used for various things, such as anonymous types in queries, making a method return multiple values or, sometimes, advantageously replacing a struct or a class. They can even help with pattern matching as explained in this article by Changhui Xu 😉

Creating and using tuples

Tuples are a really nice way of grouping some variables quickly; they are easy and intuitive to write:

For example, this code snippet shows how to create a tuple of two strings that contain a firstname and a lastname. Note that, of course, you’re not limited to 2 elements, and each element can have a different type!

So, here, we could also integrate the age of the person inside our tuple:

Once you’ve created your tuple, you can access its elements in various ways:

  • using the Item1, Item2built-in references: those give you back the different variables in your tuple via their index (starting from 1)
  • using specific user references that you define yourself either when grouping the variables together in the tuple, or when retrieving them at the end with deconstruction
  • using inferred references (only in C# 7.1+): in later versions, the name of a tuple field can be “guessed” by the program depending on the name of the variables you construct it from (but there are some limitations, and it can undermine the whole “created-on-the-spot” feature a bit, in my opinion)

Here’s an example that uses all of these techniques:

The second case here shows a crucial trick when working with tuples: using deconstruction to get back the different elements in the tuple as separate variables. This is a way of retrieving your data and unpacking it for further use.

As usual, because C# has its “implicit typer” var keyword, you can have it guess the type of objects in your tuple; but you can also provide them explicitly, or prepare them beforehand as “empty” variables and then fill them with the contents of your tuple:

You can also create tuples from other tuples using basic assignment, as long as the number of elements and their types match (optionally with some implicit conversion):

Ok – with all that said: what are good use cases for tuples? What are they relevant for?

Having a method return multiple values

A very common usage is when you want a method to return multiple values. Instead of using the out keyword, you can also group all of the returns in one tuple, return this tuple and then deconstruct it at the other end.

Let’s say you have a little function that computes the perimeter and the area of a rectangle. You want it to return both numbers in one go, rather than making two methods. So – you could use out to pass some variables by reference, and then you’d just have to prepare those variables and have them be filled by the method:

This works and it’s relatively readable. However, it requires you to take into account the fact that those variables are now passed by reference: they have to pre-exist the call, they can be modified in completely crazy ways without you knowing and they completely break your chances of ever writing functional code with pure functions… (which in turn makes it harder to unit test, etc.).

Tuples avoid this issue: rather than passing in variables to modify, you can generate one inside your method that contains two parts, the perimeter and the area. And this tuple can be returned by the method just by setting its return type to match the types of the tuple elements:

When you call the function, you get a tuple with 2 float elements that can be deconstructed or read like we saw before. Pretty cool, right ? 🙂

Creating anonymous types

Sometimes, your code might need to have some short-lived very temporary value to work on, and then have it cleaned up just as quickly. This is particularly frequent for LINQ queries: more often than not, you’ll want to compare, or sort, or group by a value that is not directly present in your data but computed from it.

Tuples are a way of having anonymous types: read-only values that are auto-typed by the compiler, usually made out of another piece of data and easy to iterate through or further inquire in your program.

Suppose you have a list of fruits that you can sell, and you know the unit price and the amount of each. What you want is to sort them according to their total price, meaning when multiplying the two values to get the value of all the apples, or oranges, or pears.

This would give you a simple LINQ query with an OrderBy():

The thing is that if you want to print the total price, you have to recompute it a second time in the foreach-loop. No worries with only 3 items, but as the list keeps growing it might start to take a toll on your program!

A cool thing would be to compute it once, then use it for sorting, then for printing – we’d make an IEnumerable of floats with the total prices:

But, now, we’ve the lost the name of our fruits in the debug! So what if you want to keep it to print it, too? Do you really have to create a little struct with two fields just to hold this specific info?

Well, no! Tuples are a neat way of shortening all of this to fit in virtually the same size of code – instead of making an enumeration of FruitInfo, we can make an enumeration of (string, float) tuples:

Here, we’re simply keeping one value as is and computing the second one; then, we use some LINQ statements to sort the collection based on an item of our tuples and we get our final result!

Final notes

Tuples VS structs and classes

As we’ve just seen, tuples can sometimes be seen as a more “dev-friendly” alternative to structs or classes. They are a really nice option when ever you have private or internal short-lived values.

However, when you have public data that you’ll want to re-access or re-modify often, you should definitely go for a struct or a class.

Mutability

Something worth noting when working with C# tuples is that they are mutable.

If a variable is mutable, then you can update its value once it’s been created; else, if it’s immutable, it sticks with the value it had upon creation for the rest of its life. For example, integers or strings are both immutable types because changing the value of a variable of this type means re-creating a new one. Arrays or lists, on the other hand, can be modified after their creation: they are mutable.

In C#, contrary to other programming languages like Python, you can update a tuple after it’s been created:

And even though the type is mutable, you can still access its hash code like you would for other C# objects. Notice how this hash code changes if you modify the tuple, but also that it is deterministic, meaning that reverting the changes brings back the original hash code:

About tuples equality

Finally, tuples can be compared and checked for equality just like other basic data types. The idea is basically to do a “component-wise” comparison: the program will check to see if each element of the tuple on the left equals the matching element of the tuple on the right and, if all do, then it returns “true” for equality.

Note that you can only check for equality between tuples of the same size: if they have mismatching number of elements, the code will not compile! Also, you have to make sure that every element on the left can be compared with == or != to its right counterpart.

For example, these won’t work (they won’t compile):

When checking if two tuples are equal, only position counts: the field names are not taken into account. So, for example, those tuples are not equal, because the values are reversed:

Conclusion

Tuples are an amazing way of making lightweight data structures: they help with the flow of the code, avoid you writing class and struct initialisers and even support functional programming!

What about you: do you use tuples a lot in C#? Do you prefer passing your variables by reference and strolling on the object-oriented lane? Feel free to tell me in the comments! 😉

Leave a Reply

Your email address will not be published.