C# Tuple: Complete Guide

A thumbnail showing code. Everything you need to know about C# Tuple data structure as a web developer.

C# tuple is a lightweight data structure that provides concise syntax to group multiple data elements.

C# Tuples are a great way to pass multiple values around, but they can be hard to understand and use. How are values in a tuple related? How do you unbox a tuple into variables that have meaningful names?

If you are a web developer, this guide will help you use tuples in your own code right away!

We'll show you what c# tuples are, how they're implemented under the hood, and all of their various uses throughout every part of .NET development.

C# tuple example code.

C# Tuples vs System.Tuple

Before we start, there's an important distinction to make:

The C# tuple type is not the same as the System.Tuple type.

C# tuples are backed by System.ValueTuple types, while System.Tuple type uses a class.

The System.ValueTuple types differ from the System.Tuple types in that:

  • System.ValueTuple are structs, System.Tuple are classes,
  • System.ValueTuple are mutable, System.Tuple are readonly
  • System.ValueTuple members are fields, members of System.Tuple are properties.

Microsoft introduced C# tuples in C# 7. So if you are using C# 7 or later, use C# tuples based on System.ValueTuple. But if you are using an older version, then you will have to use System.Tuple.

In this guide, we cover both:

  • C# Tuple
  • System.Tuple

C# Tuple

A C# tuple is a comma-separated list of elements enclosed in parentheses. Tuples can contain 0 or more elements, including nested tuples.

A tuple has the following syntax:

(type1, type2,...)

For example, to represent a person's name and age we can use a tuple as follows:

(string firstName, int age)

C# tuple can hold any number of elements. But, the number of elements in a tuple must be fixed at compile time.

How to declare a tuple in C#?

The easiest way to create a tuple is to use parentheses syntax.

You can also create a tuple by specifying the type name, followed by the element types in parentheses. For example:

var latLong = (25.2, 12.2);

Console.WriteLine(latLong); // (25.2, 12.2)

How many elements can tuple hold?

C# tuple can contain any number of elements, from zero to an infinite number of elements. The name "tuple" might make you think the tuples contain two elements, but that's not the case.

Moreover, tuples can also contain other tuples as elements.

The source code for C# tuple showing 18 elements.
A tuple can contain many items. But do you need it?

How to change C# tuple value?

In C#, tuples are mutable. They are implemented as ValueTuple which is a struct.

Tuples in C# can be updated in place by specifying new values for elements in the tuple. The following example shows how to update an existing tuple with new values:

var latLong = (25.2, 12.2);

latLong.Item1 = 42.8;

Console.WriteLine(latLong); // (42.8, 12.2)

Named Tuple

The default tuple member names is "Item1", "Item2", "Item3", etc. Default naming makes it difficult to keep track of the order of the tuple members.

C# 7 introduces a new type of tuple, called a "named tuple". Named tuples have named members, which makes them easier to read and write.

The following example shows how to create a named tuple:

(string ItemName, double Price) itemPrice = ("Socks", 12.99);

Console.WriteLine(itemPrice); // (Socks, 12.99)

Console.WriteLine(itemPrice.ItemName); // Socks

Console.WriteLine(itemPrice.Price); // 12.99

Tuple deconstruction

Tuple deconstruction is a feature for extracting the values from a tuple into individual variables.

The C# programming language has a special syntax for deconstructing tuples, which allows you to deconstruct all of the values in a tuple in one step. To deconstruct a tuple, use the same syntax as you would use to create a tuple.

In the example below, GetName returns a string extracted from the (string, double) tuple.

public static string GetName((string, double) itemPrice)
{
  (string name, _) = itemPrice; 

  return name;
}

var itemPrice = ("Socks", 12.99);

Console.WriteLine(GetName(itemPrice)); // Socks

The underscore _ must be present because it indicates that we are ignoring the second parameter (price).

System.Tuple

System.Tuple is tuple implementation that uses classes. Prior C# 7, it was the only option. That's why it's still widely used.

System.Tuple type argument

Tuple classes require type arguments which you must provide when creating tuples using new constructor. The type arguments determine the number and type of the tuple members. The type needs to match the tuple elements.

For example, the following code declares a tuple with two members of double type:

Tuple<double, double> coordinates = new Tuple<double, double>(25.2, 12.2);

If you try to create a tuple without specifying the type of arguments, you'll get a compile error. For example:

Tuple<double, double> coordinates = new Tuple(25.2, 12.2);

Compilation error: Cannot create an instance of the static class 'System.Tuple'

Tuple.Create() vs new Tuple

C# has two methods for creating tuples: Tuple.Create() and new Tuple(). They both do the same thing, but Tuple.Create() is more concise and is preferred in most cases because it is easier to read and write.

var coordinates = Tuple.Create(25.2, 12.2); // preferred way

var coordinates = new Tuple<double, double>(25.2, 12.2); // less tidy variation

The fundamental difference between Tuple.Create and new Tuple is that you can use Tuple.Create without having to specify the type arguments. If you try to use new Tuple without specifying the type of arguments, you'll get a compile error.

Under the hood, both Tuple.Create() and new Tuple() do the same thing. Tuple.Create() calls the new Tuple() constructor with default values. The following example shows how both of these methods are translated to the same code:

public static class Tuple
{
    public static Tuple<T1> Create<T1>(T1 item1) {
        return new Tuple<T1>(item1);
    }
}

How to access System.Tuple values?

To access the values in a tuple, you use the Item1, Item2, etc. notation. This is a shorthand for the members of the tuple.

Unlike C# tuples, System.Tuple's elements are not named, so you have to use ordinal index to access the value of tuple element.

var coordinates = Tuple.Create(25.2, 12.2);

double latitude = coordinates.Item1; // 25.2

double longitude = coordinates.Item2 // 12.2

How to change System.Tuple member values?

System.Tuple is immutable. They cannot be changed after they have been created.

var itemPrice = Tuple.Create("socks", 12.99);

itemPrice.Item2 = 8.99; 

// Compilation error: Property or indexer 'Tuple<string, double>.Item2' cannot be assigned to -- it is read only

To change a tuple value, you have to replace the tuple you want to modify by a new tuple containing the updated value:

var itemPrice = Tuple.Create("socks", 12.99);

Tuple.Create(itemPrice.Item1, 8.99);

System.Tuple limitations

System.Tuple is limited to a maximum of 8 items. That's because it uses a class and the .NET Framework team had to provide a finite number of classes to represent it.

If we want to make a Tuple with more elements, we must first build nested Tuples.

The last element of an eight element tuple must be a Tuple.

var numbers = new Tuple<int, int, int, int, int, int, int, int>(1, 2, 3, 4, 5, 6, 7, 8);

/* Unhandled exception. System.ArgumentException: The last element of an eight element tuple must be a Tuple.

  at System.Tuple`8..ctor(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest)

  at Program.Main()

*/

The following code declares a tuple with eight members, where the last one is another tuple which satisfies the TRest type constraint:

var numbers = new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10));

Console.WriteLine(numbers); // (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Console.WriteLine(numbers.Rest); // (8, 9, 10)

The nested tuple with 8 elements exposes the public Rest property, which makes it easier to access the last tuple's members. To do so, Rest uses internal ITupleInternal interface that allows to call tuple methods recursively without knowing the underlying types.

The source code for System.Tuple class.
The 8th member of System.Tuple must implement `ITupleInternal` interface.

Tuple use cases

Tuples are great for moving data around without creating a custom class.

The most common use cases for tuples are:

  • Return multiple values from a function without using the out parameter.
  • Grouping related values together.
  • LIQN Projection - a LINQ technique for extracting a subset of elements from a sequence.

Code readability

Tuples are a very concise way of representing data, and they can be very helpful when you want to return multiple values from a method. However, they can also make your code more difficult to read. If you're not careful, it's easy to create tuples that are difficult to understand.

If the number of elements is over 3, it's better to use a class instead of a tuple. A class is more readable and it can be easier to add new members to a class than to a tuple.

Even though C# limits tuples to 7 elements, it's a good programming practice to create classes which group several values together if the number of elements is over 3. So you might never even hit the limit for tuples. But if you do, rethink your code. It's better to use a class.

ToString()

Tuples have a built-in ToString() method that returns a string representation of the tuple. The string contains the value of each member, separated by commas.

var coordinates = Tuple.Create(25.2, 12.2);

string description = coordinates.ToString(); // (25.2, 12.2)

Values get wrapped in parentheses.

With nested tuples, the string representation is indented to show the nesting.

Tuple Vs Dictionary

In C#, tuples are often used instead of Dictionary because they are more concise.

A Dictionary<TKey, TValue> is a data structure that allows you to store key-value pairs. The key can be any type, and the value can be any type.

Dictionaries can only store one value per key, while tuples can have multiple values with the same key. That's why tuples are often used to instead of dictionaries.

Tuple Vs Enum

Tuples are often used in place of enums because C# doesn't support string enums.

For example, the following enum represents country names:

public enum Countries
{

  UnitedStates = "US",

  Canada = "CA"

}

But this code won't compile in C# because enums can only have numeric values.

A better solution is to use tuples:

List<(string CountryName, string CountryCode)> countries = new List<(string CountryName, string CountryCode)>() { ("UnitedStates", "US"), ("Canada", "CA") };

Console.WriteLine(countries[0].CountryCode); // US

Conclusion

In this article, we have explored how to use tuples in .NET development ecosystem. Tuples are a great way to return multiple values from a method, but they can also make your code harder to read. If you're not careful, it's easy to create tuples that are difficult to understand.

Different tuple implementations

It's important to understand the difference between the new C# 7 Tuples and System.Tuple class.

The System.Tuple class is a legacy implementation that is limited to a maximum of 8 items.

The new C# Tuple uses System.ValueTuple under the hood.

This implementation is more versatile and has no limit on the number of elements.

Other differences between System.ValueTuple and System.Tuple include:

  • System.ValueTuple is a struct.
  • System.Tuple is a class.
  • System.ValueTuple are mutable
  • System.Tuple are read-only.
  • Members of members of System ValuePair are fields, members of members of System Tuple are properties

When to use tuples

Tuples are a great way to group several pieces of information together, but they also make it hard to maintain your code. If you have a lot of items to store, use classes instead of tuples because they are easier to read and maintain.

If the number of items is less than 3, tuples are a good way to go.

Published on