The word "tuple" in C# refers to a data structure which may consist of multiple parts. This data structure may or may not be a dataset with multiple values. Tuples were first introduced with .NET framework 4.0, and they allow a maximum of 8 elements. More than that will give you a compiler error. Tuples are used when you want to create a data structure that holds objects and their properties, and you do not want to create a separate type.
Generic syntax for a tuple:
1Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>
Tuples have several advantages. They allow:
out
Before we had tuples, we had three options to return multiple values from a method. We could use a Class
or Struct
type, or we could use the out
parameters.
Tuples are also ideal for storing object properties up to 8 counts because they allow the items placed in them to be of different types. With, for example, lists or arrays, this task is difficult, as there is no limit to the number of elements they can store, but the type restricts the options. Tuples are used, for example, in one of our company's web applications that handles registration, and we require exactly 8 elements from the newcomer.
There are two ways one can utilize the tuple data structure. There is a class-based approach, which utilizes the constructor of the Tuple<T>
class, and there is the Create
method, also a viable option.
We can utilize the Tuple<T>
class to create our tuple with the specific length and type of the elements we would like to store.
Let's take a code-based example.
1using System;
2
3namespace tuples
4{
5 class Tupling
6 {
7 static void Main(string[] args)
8 {
9 Tuple<string> MyStringTuple = new Tuple<string>("Pluralsight");
10 Tuple<string,int> MyCustomTuple = new Tuple<string,int>("Daniel",28);
11 Tuple<int, int, int, int, int, int, int, Tuple<int>> MyMaxTuple = new Tuple<int, int, int, int, int, int, int, Tuple<int>>(1, 2, 3, 4, 5, 6, 7, new Tuple<int>(8));
12 Console.WriteLine($"The {nameof(MyStringTuple)} has the following elements: {MyStringTuple}");
13 Console.WriteLine($"The {nameof(MyCustomTuple)} has the following elements: {MyCustomTuple}");
14 Console.WriteLine($"The {nameof(MyMaxTuple)} has the following elements: {MyMaxTuple}");
15 Console.ReadKey(); ;
16 }
17 }
18}
Let's inspect the output.
1The MyStringTuple has the following elements: (Pluralsight)
2The MyCustomTuple has the following elements: (Daniel, 28)
3The MyMaxTuple has the following elements: (1, 2, 3, 4, 5, 6, 7, 8)
You can see from the initialization that we have three tuples.One is MyStringTuple
, another is MyCustomTuple
, and finally MyMaxTuple
. All are unique and provide all the information you need about tuples. When you have a tuple of 8 items, the last item always must be a tuple.
When you use the constructor-based approach, it can make your code harder to read because you have to specify the type of each element for the tuple, and the longer the tuple the more it will flood your code. To ease the pain of developers, the create
method was born for the Tuple
class. It contains static methods that allow you to create the instance without providing the type of each element.
Our previous example rewritten in this style looks like this.
1using System;
2
3namespace tuples
4{
5 class Tupling
6 {
7 static void Main(string[] args)
8 {
9 var MyStringTuple = Tuple.Create("Pluralsight");
10 var MyCustomTuple = Tuple.Create("Daniel",28);
11 var MyMaxTuple = Tuple.Create(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8));
12 Console.WriteLine($"The {nameof(MyStringTuple)} has the following elements: {MyStringTuple}");
13 Console.WriteLine($"The {nameof(MyCustomTuple)} has the following elements: {MyCustomTuple}");
14 Console.WriteLine($"The {nameof(MyMaxTuple)} has the following elements: {MyMaxTuple}");
15 Console.ReadKey();
16 }
17 }
18}
This looks more concise and allows improved readability for the next developer working on the application.
When we instantiate a new tuple either via the class-based method or the create method, our tuple will have a unique property. It will assign each consecutive element to a property of the instance with the following logic:
1Item<elementNumber>
This means that a tuple that has 4 elements will have the following properties:
1Tuple.Item1
2Tuple.Item2
3Tuple.Item3
4Tuple.Item4
We then modify our code according to this:
1using System;
2namespace tuples
3{
4 class Tupling
5 {
6 static void Main(string[] args)
7 {
8 var MyStringTuple = Tuple.Create("Pluralsight");
9 var MyCustomTuple = Tuple.Create("Daniel",28);
10 var MyMaxTuple = Tuple.Create(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8));
11 Console.WriteLine($"The first element of our {MyStringTuple} is {MyStringTuple.Item1}");
12 Console.WriteLine($"The first element of our {MyCustomTuple} is {MyCustomTuple.Item1}, the second is: {MyCustomTuple.Item2}");
13 Console.WriteLine($"The first element of our {MyMaxTuple} is {MyMaxTuple.Item1}, the last is: {MyMaxTuple.Rest}");
14 Console.ReadKey();
15 }
16 }
17}
This gives us the following output:
1The first element of our (Pluralsight) is Pluralsight
2The first element of our (Daniel, 28) is Daniel, the second is: 28
3The first element of our (1, 2, 3, 4, 5, 6, 7, (8)) is 1, the last is: ((8))
Note how the last element of the eight-object tuple can only be referred to as .Rest
, and it must always be a tuple.
The limitation of the maximum eight elements can be overcome by nesting tuples into each other. Best practice is to nest a tuple into another at the last element. However, this is not enforced in any way, so you are able to nest a tuple anywhere inside another tuple. When you nest at the end, you can access the nested tuple via the .Rest
property; otherwise, you need to use the .Item<value>
property to access the tuple.
Let's demonstrate it with a code.
1using System;
2
3namespace tuples
4{
5 class Tupling
6 {
7 static void Main(string[] args)
8 {
9 var MyMaxTupleNested = Tuple.Create(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10, 11, 12, 13, 14, Tuple.Create(15, 16, 17, 18, 19, 20, 21)));
10 Console.WriteLine($"My original tuple: {MyMaxTupleNested}");
11 Console.WriteLine($"First element of the outest tuple: {MyMaxTupleNested.Item1}");
12 Console.WriteLine($"First level of nesting: {MyMaxTupleNested.Rest.Item1}");
13 Console.WriteLine($"Second level of nesting: {MyMaxTupleNested.Rest.Item1.Rest.Item1}");
14 Console.ReadKey();
15 }
16 }
17}
The output should look like this:
1My original tuple: (1, 2, 3, 4, 5, 6, 7, (8, 9, 10, 11, 12, 13, 14, (15, 16, 17, 18, 19, 20, 21)))
2First element of the outest tuple: 1
3First level of nesting: (8, 9, 10, 11, 12, 13, 14, (15, 16, 17, 18, 19, 20, 21))
4Second level of nesting: (15, 16, 17, 18, 19, 20, 21)
In my opinion, the method of nesting at the last element provides a clear code and a clear way to troubleshoot. It might not be a one-solution-fits-all kind of approach, however, because in some situations you may better off nesting in the middle.
In this guide, we started out by getting familiar with tuples, a data structure that has been part of C# since .NET 4.0. We then inspected two different ways to create new instances of this class and learned how to access elements of this data structure. We ended with some nesting and additional tips on how to be efficient. I hope this has been informative for you and that you found what you were looking for. Thanks for reading.