Tuples in C#: Organize Data with Ease & Efficiency

Per definition, Tuples are a data structure that provides an easy way to represent a single set of data in C#. They were first introduced in .NET Framework 4.0 specifically for F#. In C#, Tuples are represented by the System.Tuple and System.ValueTuple classes.

System.Tuple and System.ValueTuple have some differences related to syntax, mutability, and memory allocation which we are going to talk about in more detail below.

System.Tuple vs System.ValueTuple

System.Tuple and System.ValueTuple is different in some ways and similar in many. Below, let’s check the 3 key differences between them, and then you can decide which one to use in your apps.

Syntax

When creating and accessing elements, System.Tuple uses a more verbose way, which is not very intuitive especially when you have more than 2 elements. Example:

var tuple = Tuple.Create(1, "hello");
int number = tuple.Item1;
string text = tuple.Item2;

The Tuple.Create() is used to create the tuple, where you define two values of type integer and string. Now, to access these values, you need to use Item1 and Item2. Imagine having way more values, you would have to remember the order of the items to be able to correctly access them.

In System.ValueTuple, if we were to create the same example, it would look like the below:

var tuple = (Number: 1, Text: "hello");
int number = tuple.Number;
string text = tuple.Text;

In this example, you can see that we provided the same values, but for each value, we also assigned a name Number, and Text. So, when you can access the elements using their names, like Number and Text in this example.

Also, to create the tuple I have simply used parenthesis (..props..) instead of using Tuple.Create method.

Mutability

System.Tuple is immutable, meaning that you cannot modify its elements once the tuple is created. If you need to change the values of a System.Tuple, you must create a new instance with the modified values.

var tuple = Tuple.Create(1, "hello");
// Trying to change the elements would cause a compilation error:
// tuple.Item1 = 2; // Error: Property or indexer 'Tuple<int, string>.Item1' cannot be assigned to -- it is read-only

On the other hand, System.ValueTuple is mutable, allowing you to change the values of its elements after it has been created. However, if the System.ValueTuple is used in a read-only context, like as a return type of a method or as a property, the elements will also be read-only.

var tuple = (Number: 1, Text: "hello");
// You can change the elements' values:
tuple.Number = 2;
tuple.Text = "world";

In this case, you can change the values of the elements in a System.ValueTuple without any issues.

Memory Allocation

System.Tuple is a reference type, which means that instances are allocated on the heap. It may lead to higher memory usage and can cause performance issues, especially when working with large data sets or in performance-critical scenarios.

But, System.ValueTuple is a value type, which means that instances are typically allocated on the stack as value types. This leads to better performance and lower memory usage when compared to System.Tuple.

In conclusion, System.ValueTuple is the recommended choice for most cases due to all the reasons mentioned above. But, for sure there are special cases, especially when working with older libraries or APIs that expect System.Tuple instances, when you need to work with System.Tuple instead.

Now, below let us talk about some use cases of Tuples (I am going to refer System.ValueTuple as just Tuple).

Tuple Use Cases

There are different use cases of Tuples, but below you will learn about the common use cases of Tuples. You have already seen that you can use a Tuple to group multiple data elements in a lightweight data structure. But, what else can it be used for?

Multiple return values

A common use case of a tuple is when you want to have a function that will return multiple values. For example, you can create a function that will return the max value of an array and another function that will return the min value of an array. Instead, you can create a function that will return a tuple that has the minimum value and the maximum value of an array.

public (int Min, int Max) GetMinAndMax(List<int> numbers)
{
    int min = numbers[0];
    int max = numbers[0];

    foreach (int number in numbers)
    {
        if (number < min) min = number;
        if (number > max) max = number;
    }

    return (min, max);
}

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
(int min, int max) = GetMinMax(numbers);
Console.WriteLine($"Min: {min}, Max: {max}");

Lightweight data grouping

Another case would be if you want to return an anonymous object. Anonymous objects can be helpful when you need a simple data structure to hold related data without the overhead of creating a custom class or struct.

Suppose you want to represent a 2D point with X and Y coordinates. Instead of creating a custom class or struct for this, you can use a Tuple

var point = (X: 3, Y: 4);

Now, that you have the Tuple you can easily access the properties

Console.WriteLine($"X: {point.X}, Y: {point.Y}");

You can also use Tuples to store more than one object. In this example, I will store 3 points in a 2D graph

var points = new List<(int X, int Y)>
{
    (10, 30),
    (-5, 28),
    (0, 33)
};

foreach (var point in points)
{
    Console.WriteLine($"X: {point.X}, Y: {point.Y}");
}

Here, we have defined a new List of ValueTuples called points. Each Tuple has two named elements: X and Y, both of integer type. The list is initialized with three 2D points represented as Tuples.

Merging collections

Suppose you have two lists, one has the student names and the other their respective GPAs. You can use a Tuple to create a single list combining both pieces of information.

List<string> studentNames = new List<string> { "Olivia", "Noah", "Emma", "Liam" };
List<decimal> studentGPAs = new List<decimal> { 3.99m, 2.49m, 3.79m, 3.50m };

List<(string Name, decimal GPA)> students = studentNames.Zip(studentGPAs, (name, gpa) => (name, gpa)).ToList();

foreach (var student in students)
{
    Console.WriteLine($"{student.Name}: {student.GPA}");
}

The code above, will merge the two lists into a Tuple and show the student Name and GPA values in a for-each loop.

These are just a few examples of how Tuples can be used in C#. Tuples are very useful in many scenarios where you want to work with multiple related values without having to create custom classes or structures.


Enjoyed this post? Subscribe to my YouTube channel for more great content. Your support is much appreciated. Thank you!


Check out my Udemy profile for more great content and exclusive learning resources! Thank you for your support.
Ervis Trupja - Udemy



Enjoyed this blog post? Share it with your friends and help spread the word! Don't keep all this knowledge to yourself.