C# Tuples Tutorial

In this tutorial we learn about a newer complex data structure in C# that can hold different types of elements, a tuple.

We cover how to initialize a tuple, how to nest and access tuple values with Rest and how to use a tuple in a method.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

What is a Tuple

A Tuple is a generic data structure that can hold different types of elements. Think of it as an array with different types.

A tuple is a reference type and lives on the heap.

Syntax:
Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>

A tuple has the restriction of only being able to hold 8 elements.

How to initialize a Tuple

We can initialize our tuples in one of two ways, the standard declaration, or the built-in Create() method.

Let’s start with the standard tuple declaration. A tuple is a generic type so we are required to specify the element types between the angle brackets.

When initializing a tuple, we also specify the values between parentheses (the tuple’s constructor).

Syntax:
Tuple<element_type_1, element_type_2> tuple_name = new Tuple<element_type_1, element_type_2>(value_1, value_2);

The values must correspond to the types laid out between the angle brackets.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            Tuple<string, string, int, float> example = new Tuple <string, string, int, float>("Hello", "World", 20, 3.14f);
        }
    }
}

Let’s break it down step by step:

  1. We use the Tuple keyword followed by the types we want between angle brackets, separated by commas.
  2. We give our tuple a name and create a new instance of it with the types again between angle brackets.
  3. Lastly, we specify our values in the constructor (between the parentheses). These values must correspond to the types.

Next, let’s see the tuple helper class and Create() method.

Specifying the type for each element is cumbersome, but a static helper class exists so we don’t have to.

The Tuple class has a Create() method that will automatically infer the type from the value, like var.

Syntax:
var tuple_name = Tuple.Create(value_1, value_2);
Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            var message = Tuple.Create("Hello", "World", 1, 3.14f);
        }
    }
}

We instantiate the tuple directly with the values we want, passed to the method via its parameters.

How to access elements in a Tuple

Elements in a tuple are stored as items. To access an item we use the word Item with the element’s index.

Syntax:
// initialization
var tuple_name = Tuple.Create(value_1, value_2);

// access
tuple_name.Item1;
Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            var message = Tuple.Create("Hello", "World");

            Console.WriteLine(message.Item1 + " " + message.Item2);
            Console.ReadLine();
        }
    }
}

In the example above, we create a tuple with two elements. Item1 would correspond to “Hello” and Item2 would correspond to “World”. Then, we use the object message to access the Item1 and Item2 members and print them out to the console.

It will only access up to Item7. The 8th element is accessed by using the Rest property, because Rest is used for nesting another tuple.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            var nums = Tuple.Create(1, 2, 3, 4, 5, 6, 7, 8);

            Console.WriteLine("Num: " + nums.Rest);
            Console.ReadLine();
        }
    }
}

In the example above, instead of using Item8, we use the word Rest to access the 8th element.

How to nest a Tuple with Rest

We can nest one tuple inside another. A tuple can be nested anywhere, but it’s recommended to nest it as the 8th element so that it can be accessed with the Rest property.

Syntax:
var tuple_name = Tuple.Create(v1, v2, v3, v4, v5, v6, v7, Tuple.Create(v1, v2));
Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            var message = Tuple.Create(1, 2, 3, 4, 5, 6, 7, Tuple.Create("Hello", "World"));
        }
    }
}

In the example above, we create a normal tuple. In the 8th element we create a new tuple as a nested tuple.

How to access a nested Tuple with Rest

To access the nested element we use Rest items.

Syntax:
// initialization
var tuple_name = Tuple.Create(v1, v2, v3, v4, v5, v6, v7, Tuple.Create(v1, v2));

// access
tuple_name.Rest.Item1.Item1;

We specify Rest.Item1.Item1. Rest.Item1 is the 8th element, and Rest.Item1.Item1 is the first element of the nested tuple at the 8th element.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            var nums = Tuple.Create(1, 2, 3, 4, 5, 6, 7, Tuple.Create("Hello", "World"));

            Console.WriteLine(nums.Rest.Item1.Item1);
            Console.ReadLine();
        }
    }
}

In the example above, we create a normal tuple. In the 8th element we create a new tuple as a nested tuple. Then, we access the first element of the tuple that was nested at the 8th element with Rest.Item1.Item1.

To see more clearly how it works, remove the last Item1 from the print and run the application. Then add it back and run the application again.

How to access a nested Tuple without Rest

If we don’t nest another tuple as the 8th element, we don’t have to use Rest.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            var nums = Tuple.Create(1, 2, 3, 4, Tuple.Create("Hello", "World"), 6, 7, 8);

            Console.WriteLine(nums.Item5.Item1);
            Console.ReadLine();
        }
    }
}

In the example above, we nest our second tuple as the 5th element. The nested tuple will have to be accessed through Item5. We access our nested tuple’s first element with Item5.Item1 and the second element with Item5.Item2.

How to use a Tuple in a method

A tuple can be used in methods as a return type or an input parameter.

To use a method as a return type, we specify the tuple in place of a regular type.

Syntax:
Tuple<T1, T2, T3 etc.> method_name()
{
    // method body
}
Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            Console.WriteLine(Message().Item1 + " " + Message().Item2);
            Console.ReadLine();
        }

        static Tuple<string, string> Message()
        {
            return Tuple.Create("Hello", "World");
        }
    }
}

In the example above, we create a method called Message with the return type of a tuple with two elements of type string. We create the tuple to return and give it some string values.

In the Main() method we access each element in the method as we would normally access them through an object, Message().Item.

To use a tuple as method input parameter, we specify the tuple as the input parameter type.

Syntax:
void method_name(Tuple<T1, T2, T3 etc.> param_name)
{
    // method body
}
Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            Message(Tuple.Create("Hello", "World"));
        }

        static void Message(Tuple<string, string> msg)
        {
            Console.WriteLine(msg.Item1 + " " + msg.Item2);
            Console.ReadLine();
        }
    }
}

In the example above, we create a method called Message with an input parameter of type tuple which has two elements of type string.

Then, we access each element as we normally would and print them to the console. In the Main() method we call the function and create a new tuple with two string values.

C# 7 includes ValueTuple to make it easier to work with and overcome some of its limitations, such as the limited number of elements.

Summary: Points to remember

  • A tuple is a generic data container that can hold up to 8 values of different types (if another tuple is not nested).
  • A tuple can be nested within another tuple, and is typically nested as the 8th element.
    • When a tuple is nested at the 8th element, we use Rest to access the nested tuple.
    • When a tuple is not nested at the 8th element, we access it directly with .Item.
  • A tuple can be used as a method parameter or return type.
  • C# 7 has a better version of the tuple, called a ValueTuple.