C# ValueTuples Tutorial

In this tutorial we learn about the struct representation of a Tuple in C#, called the ValueTuple.

e cover how to initialize it, the valuetuple helper, named members as lvalues or rvalues and how to use the valuetuple 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 ValueTuple

A ValueTuple is a struct representation of the Tuple class. Where Tuple is a reference type, ValueTuple is a value type.

A ValueTuple overcomes some of the limitations of Tuple and makes tuples easier to work with.

Note that ValueTuple is only available in C# 7.0 (.NET Framework 4.7) and up.

How to initialize a ValueTuple

Initializing a ValueTuple is similar to initializing a normal Tuple.

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

We don’t specify the keyword ValueTuple when we assign values like we do with a normal tuple. We only write the values, separated by a comma, between parentheses.

There is also a shorthand way of writing the code above.

Syntax:
(element_type_1, element_type_2) tuple_name = (value_1, value_2);

This time we don’t specify the keyword ValueTuple anywhere and the element types are between parentheses.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            ValueTuple<string, string> message1 = ("Hello", "World");
            Console.WriteLine(message1.Item1 + " " + message1.Item2);

            (string, string) message2 = ("Hello", "there");
            Console.WriteLine(message2.Item1 + " " + message2.Item2);

            Console.ReadLine();
        }
    }
}

In the example above, we create a ValueTuple called message1 with two string elements and assign two string values to it. Then, we print the two elements to the console.

Then, we create a ValueType in the shorthand way by simply specifying the two string types, the name message2, and the two string values. Then, we print the two elements to the console.

How to use the ValueTuple helper

The ValueTuple helper is similar to the Tuple helper Tuple.Create() class method. However, with a ValueTuple we don’t specify the Create() class method.

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

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            var message = ("Hello", "World");
            Console.WriteLine(message.Item1 + " " + message.Item2);

            Console.ReadLine();
        }
    }
}

In the example above, we instantiate the ValueTuple directly with the values we want, passed as parameters.

A ValueTuple requires at least two values to be recognized as a Tuple.

Example:
// tuple
var message = ("element1", "element2");

// not a tuple
var message = ("element1");

One of the benefits of a ValueTuple over a Tuple is that it can hold more than 8 values.

Example:
var nums = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

ValueTuple named members

So far we’ve accessed ValueTuple elements with the keywords Item1, Item2 etc. But, we can provide custom names for our elements as either an lvalue or rvalue.

Syntax: Lvalue
// initialization
(element_type custom_name, element_type custom_name) tuple_name = (value_1, value_2);

// access
tuple_name.custom_name;

The custom names are given on the left along with the types.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            (string FirstName, string LastName) person = ("John", "Johnson");

            Console.WriteLine(person.FirstName + " " + person.LastName);
            Console.ReadLine();
        }
    }
}

In the example above, we give each type a custom name when we initialize the ValueTuple.

Instead of Item1, Item2 when we access the ValueTuple, we can use the custom names FistName and LastName.

Syntax: Rvalue
// initialization
var tuple_name = (custom_name : value_1, custome_name : value_2);

// access
tuple_name.custom_name;

The custom name, and the value for it, is separated by a colon.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            var person = (FirstName:"John", LastName:"Johnson");

            Console.WriteLine(person.FirstName + " " + person.LastName);
            Console.ReadLine();
        }
    }
}

This time the custom names FirstName and LastName are declared when we assign the values to the ValueTuple on the right. Again, we use the new custom names to access the tuple elements.

When an LValue custom name has been specified, we can’t specify RValue names as well. The RValue names will be ignored.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            (string FirstName, string LastName) person = (FN: "John", LN: "Johnson");
            Console.WriteLine(person.FirstName + " " + person.LastName);

            // Compiler error
            Console.WriteLine(person.FN + " " + person.LN);

            Console.ReadLine();
        }
    }
}

Because the LValue names exist, the compiler will ignore the RValue names. Visual Studio will underline them in green.

We can still access the LValue names FirstName and LastName but when we try to access the RValue names FN and LN, the compiler will raise an error.

How to use a ValueTuple in a method

Like a Tuple, a ValueTuple can be used as a method return type and a method input parameter.

To use a ValueTuple as a method return type, we replace the normal method type with multiple ValueTuple types, separated by a comma, between parentheses.

Syntax:
(type_1, type_2) method_name()
{
    // method body
}
Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            Console.WriteLine(Person().FN + " " + Person().LN);
            Console.ReadLine();
        }

        static (string FN, string LN) Person()
        {
            return ("John", "Johnson");
        }
    }
}

In the example above, we specify the types we want to use with custom names. In the method body we return the two values that match the return types.

In the Main() method we access our method ValueTuple elements via their custom names.

Tuple as method input parameter

To use a ValueTuple as a method input parameter, we replace the normal parameter type with multiple ValueTuple types, separated by a comma, between parentheses.

Syntax:
// declaration
static void method_name((type_1, type_2) parameter_name)
{
    // method body
}

// call
method_name((value_1, value_2));

The values are placed between parentheses because they are elements of one parameter and not two separate parameters.

Example:
using System;

namespace Tuples
{
    class Program
    {
        static void Main()
        {
            Person(("John", "Johnson"));
        }

        static void Person((string FN, string LN) person)
        {
            Console.WriteLine(person.FN + " " + person.LN);
            Console.ReadLine();
        }
    }
}

In the example above, we declare our method with a parameter person with two ValueTuple types called FN and LN, which we then print to the console.

In the Main() method we call our function with the two values required in between parentheses. If we don’t use the parentheses, the compiler will raise an error.

Tuple or ValueTuple, which one to use

It largely depends on your preference, which one you like best. We love the ValueTuple, it’s not only easier and faster to write, but as it’s a value type it will allocate on the stack and not the heap.

Summary: Points to remember

  • A valuetuple is a struct representation of a tuple, and is a value type instead of a reference type.
  • A valuetuple is easier to work with than a tuple and overcomes some of its limitations.
  • Valuetuples are only available in C# 7.0 (.NET 4.7) and up.
  • A valuetuple requires at least two values for the compiler to recognize it as a tuple.
  • A valuetuple is instantiated directly between parentheses.
  • Valuetuples can also be used as method parameters and return types (also between parentheses).