Our Thoughts

  • Elegant Software Solutions

C# 7 Makes Tuples Cool!

Updated: May 29, 2020

First things first: What the heck is a “Tuple”?

Tuples first came to C# in .NET 4.0 and provided a way to create an immutable group of objects without creating a new class or struct. It’s tempting to refer to a Tuple as a “collection of objects” but that would imply that each object is of the same type and Tuples allow you to mix different types. Tuples are immutable and even the order of each object in the Tuple class cannot be changed after the Tuple has been created.

Let’s use the standard example of a simple “Person” class:

If you wanted to pass some property values of a Person to another class, you would normally need to create an instance of the class then populate it with the values. Using an object initializer, it would look something like this:

And now you can pass “p1” or “p2” as a parameter to another class, or use them as a return value from your class. Nothing new here.

But what if all you really want to do is return two strings and an integer from a method, but you don’t want to create a whole new class to do so? Or what if you want to pass these multiple parameters to a BackgroundWorker that only accepts one argument? Using Tuples, these values could be represented without creating a new class like this:

You can still pass “p1” or “p2” as a parameter, and they still contain the types and values you want to pass, but you didn’t have to create a whole new class.

Why aren’t Tuples used more often? While the idea of an immutable group of objects without having to create a whole new class or struct sounds like it could be super helpful, the implementation of Tuples in C# made them a bit awkward to use. Some even consider the use of Tuples to be a “code smell” because there is no intuitive way to reference the contents of the Tuple so that it makes sense when looking at the code. In our above example, if we were returning the p1 or p2 Tuple object from a method, we could only refer to the values using p1.Item1, p1.Item2, and p1.Item3:

We can tell from intellisense that “Item1” and “Item2” are strings and that “Item3” is an integer, but that’s really all we know unless we look up the code where this Tuple is created and find out exactly what those values are supposed to represent. You may not even have access to the code that produced the Tuple in the first place, in which case it’s off to the documentation you go to look up the API and find out what exactly is “Item1” supposed to be. And nobody likes digging into documentation.