Tuples with C# 7.0

To combine multiple objects of the same type, you can use arrays. To combine multiple objects of different types, tuples can be used. Tuples allow to return multiple results without the need to create a custom class, and without the need to specify out or ref parameters. The Tuple type has been available with .NET since .NET 4.0. With C# 7.0 we get special syntax to work with tuples.
This articles shows the new tuples available with C# 7.0, and how you can work with this new syntax.

Computercogs

Creating Tuples

Tuples are created using braces. The tuple type is defined by specifying the types and names. To create an instance of a tuple, a tuple literal can be used. The following code snippet creates a tuple by specifying the value 3 to x1, and the string one to s1.
After creating the tuple, the variables x1 and s1 can be used just like ordinary variables:

(int x1, string s1) = (3, "one");
Console.WriteLine($"{x1} {s1}");

It’s also possible to specify a tuple type using existing variables:

int x2;
string s2;
(x2, s2) = (42, "two");
Console.WriteLine($"{x2} {s2}");

Returning Tuples

The following method Divide returns t tuple by specifying (int, int) as the return type, and returns a tuple passing result and reminder variables:

static (int, int) Divide(int x, int y)
{
    int result = x / y;
    int reminder = x % y;

    return (result, reminder);
}

This code snippet shows calling the method Divider and assigning the result of this method to a tuple:

(int result, int reminder) = Divide(11, 3);
Console.WriteLine($"{result} {reminder}");

ValueTuple behind the Scenes

Behind the scenes, a ValueTuple is created. This type is different from the Tuple type in that it is declared as a struct instead of a class.

ValueTuple<int, int> tuple1 = Divide(11, 3);
Console.WriteLine($"{tuple1.Item1} {tuple1.Item2}");

Different ValueType tuples are defined with one, two, three – up to seven generic parameters with an eight parameter type TRest where TReset can be another tuple. So it’s possible to combine any number of parameters within a tuple.

With older .NET applications you need to add the NuGet package System.ValueTuple. This package is already available as release candidate.

Contrary to the reference type Tuple, ValueTuple is a value type, a struct. The ValueTuple can decrease allocations and thus provides better performance and memory usage.

Tuple Literals

Assigning the values of a tuple to a variable declared as var or a variable of type ValueTuple, the names of the fields are not known. ValueType declares properties Item1, Item2… that can be used to access the members.

var tuple2 = ("Stephanie", 7);
Console.WriteLine($"{tuple2.Item1}, {tuple2.Item2}");

Using the tuple literal, it is also possbile to define the names:

var tuple3 = (Name: "Matthias", Age: 6);
Console.WriteLine($"{tuple3.Name} {tuple3.Age}");

Assignment with Types Matching

Just the types need to match to assign a variable of a tuple to another. In the following code snippet, result and reminder (of type int) ar assigned as a tuple to the variable tuple1. tuple1 previously has been declared as ValueTuple.
In the code line that follows, a tuple with result and reminder are assigned to a tuple with x and y – just the types need to match.

tuple1 = (result, reminder);
(int x, int y) = (result, reminder);
Console.WriteLine($"{x} {y}");

Using var with Tuples

The types of the members of the tuple can also be declared using the var keyword. the type is automatically injected based on the initialization. The variable tuple1 is of type ValueTuple and thus both a and b are of type int.

(var a, var b) = tuple1;
Console.WriteLine($"{a.GetType().Name} {b.GetType().Name}");

Deconstucting Tuples

The Deconstruct method is a new method that is used by the compiler to deconstract a type to a tuple. The Person class defines a constructor with two string parameters to initialize an instance. In accordance to this constructor, a Deconstruct method is defined – with two out parameters returning firstname and lastname.

class Person
{
    private readonly string _firstName;
    private readony string _lastName;

    public Person(string firstname, string lastname)
    {
        _firstName = firstname;
        _lastName = lastname;
    }

    public override String ToString() => $"{_firstName} {_lastName}";

    public void Deconstruct(out string firstname, out string lastname)
    {
        firstname = _firstName;
        lastname = _lastName;
    }
}

You can’t implement Deconstruct methods using a method that returns tuples. The issue with that is that you often have multiple constructors with different parameter types and numbers. Corresponding to the constructors, you might implement multiple Deconstruct methods using similar types. With C#, methods cannot be overloaded just by having different return types. Thus, the decision has been made to implement Deconstruct methods with out parameters.

The Deconstruct method is invoked when a Person object is deconstructed to a tuple, as shown as creating a tuple with first and last members:

var p1 = new Person("Katharina", "Nagel");
(string first, string last) = p1;
Console.WriteLine($"{first} {last}");

Deconstruct using Extension Methods

The Deconstruct method can also be created as an extension method – without changing the original type.
In the following code sample, the Rectangle type defines a constructor with two int parameters, but doesn’t define a Deconstruct method:

public class Rectangle
{
    public Rectangle(int height, int width)
    {
        Height = height;
        Width = width;
    }

    public int Width { get; }
    public int Height { get; }
}

The Deconstruct method for the Rectangle type is defined as an extension method in the class RectangleExtensions:

public static class RectangleExtensions
{
    public static void Deconstruct(this Rectangle rectangle, out int height, out int width)
    {
        height = rectangle.Height;
        width = rectangle.Width;
    }
}

Now the Rectangle can be used for deconstruction with a tuple of two int:

var r1 = new Rectangle(100, 200);
(int height, int width) = r1;
Console.WriteLine($"height: {height}, width: {width}");

Tuples with LinkedList

Just one more example on using the tuple with a LinkedList – combining the current value, and the next node in a tuple:

var list = new LinkedList<int>(Enumerable.Range(0, 10));

int value;
LinkedListNode<int> node = list.First;
do
{
    (value, node) = (node.Value, node.Next);
    Console.WriteLine(value);                 
} while (node != null);

Summary

Language extensions for tuples are a great enhancement with C# 7.0. After getting the Tuple type with .NET 4.0, now tuples are an integral part of the language.

Sample Code

The sample code is available at GitHub.

To run the sample code you need Visual Studio “15” Preview.

Have fun with programming and learning!
Christian

More Information

More information about C# is available in my new book and my C# workshops:

Professional C# 6 and .NET Core 1.0

Christian Nagel’s Workshops

More C# 7.0 Features:

Binary Literals and Digit Separators explain another new feature of C# 7.0.

Image from © Cammeraydave | Dreamstime.com Computer Cogs Technology Banner Background Photo