Launched a few months ago, the new version of C# brings new features primarily aimed towards simplifying code and improving performance. In this article, we will discuss the tuples, C# 7.0 version’s new features and a small “behind the scenes” explaining how they work.

What was included in the previous versions?

Very often, we need to return several values. In C#’s previous versions, there were several workarounds, but they all had their limits:

  • The Out parameter modifier:
public void GetCoordinates(out int x, out int y)
    {
        x = 2;
        y = 2;
    }

The key word Out makes it possible to pass arguments by reference. But, its use is not fluid and cannot be used with asynchronous methods.

  • Creating new types
   public class Coordinate
    {
        public int x;
        public int y;
    }

Creating a new class to regroup the results needing to be returned simplifies the code. However, this is not the wisest choice in terms of design. It adds several lines of codes whereas the purpose is to regroup temporarily some values that don’t necessarily have any functional meaning in the application. ​

  • Anonymous types
    public object GetCoordinates()
    {
        var value = new {x = 2, y = 3};
        return value;
    }

Anonymous types allow you to encapsulate multiple properties in a single object without explicitly defining the types. However, this solution is not adequate because it is opposite to the principle of strong typing.

Tuples, an existing solution?

The notion of tuple isn’t new. The System.Tuples class made developers happy when it first appeared in C#4. It is a data structure allows grouping a sequence of elements without having to create a new class or struct.

We can, as shown in the example below, declare a GetEmployeeInfo method that instantiates and returns a Tuple composed of a string and an int.

    public Tuple<string, int> GetEmployeeInfo(string id)
    {
        //Search by id and find the employee
        return new Tuple<String, Int32>("Mark", 38000);
    }

It is then possible to call the method and access its contents as follows:

    public void Test()
    {
        Tuple<string, int> info = GetEmployeeInfo("2300-d-f");
        Console.WriteLine($"Name : {info.Item1} , Salary {info.Item2}");
    }

System.Tuples is certainly been very useful, but it also has some disadvantages:

  • It is mandatory to use the Item keyword followed by a number indicating the order of the element in the tuple (Item1, Item2 …) to access the properties of the returned object which doesn’t reflect the meaning of the element and become confusing when there are several elements in the tuple. (In the previous example, it would have been better to write: info.name and info.salry)
  • The number of items to return is capped up to 8 properties. To return more, the last element must be a tuple, and this makes the syntax more difficult to understand.
  • It is mandatory to declare and instantiate a tuple object before it can be returned.
  • Tuples are reference type, which can be expensive.

The Tuples in C#7

The tuples principle

To allow multiple values ​​to be returned, the C#7 retraces the concept of System.Tuples, exposing tuples values, a finite ordered set of typed values. More concretely, the syntax has been greatly simplified. To create a tuple, we now simply put the content between two parentheses. Then, the return type of the method will be in the form of a parenthesis containing the types of elements of the tuple in their successive order:

    public (string, int) GetEmployeeInfo(string id)
    {
        return ("Mark", 38000);
    }
    public void Test()
    {
        (string, int) info = GetEmployeeInfo("2300-d-f");
        Console.WriteLine($"Name : {info.Item1} , Salary {info.Item2}");
    }

The elements of the tuple are accessible thanks to the keyword Item (Item 1, Item 2). It is also possible to name them for simplicity.

    public (string, int) GetEmployeeInfo(string id)
    {
        return (name:"Mark", salary: 38000);
    }
    public void Test()
    {
        (string, int) info = GetEmployeeInfo("2300-d-f");
        Console.WriteLine($"Name : {info.name} , Salary {info.salary}");
    }

At compilation, the tuple is replaced by ValueTuple, which differs from System.tuples by the following:

  • It is a structure (value type), not a class (Reference type).
  • It is mutable. The value of its elements is not fixed and can be modified.
  • Its data members are fields, not properties.

The deconstruction defined by the user

We have already seen several ways to deconstruct a tuple but thanks to the keyword deconstruct, it is possible to specify to the code an implicit converter of a class to a tuple.

Unlike the other conversion operators (keyword implicit: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/implicit), the deconstruct handles the tuples as follows:

public class Employee
{
    public string FirstName { get; }
    public string LastName { get; }
    public double Salary {get; }
    public Person(string first, string last, double salary)
    {   
        FirstName = first;
        LastName = last;
        salary = salary;
    }
    public void Deconstruct(out string firstName, out string lastName)
    {
        firstName = FirstName;
        lastName = LastName;
    }
}

We can use it like this:

        var e = new Employee("john", "Doe", 1);
        var (firstname, lastName) = e;

Microsoft even allows us to declare these deconstruction methods in extension and to infer the correct method according to the number of parameters.

public static class ExtentionsTuple
{
    public static void Deconstruct(this employee, out string firstName, out string lastName, out double salary)
    {
        first = employee.firstName;
        lastName = employee.lastName;
        salary = employee.Salary,
    }
}
        var e = new Employee("john", "Doe", 1);
        var (firstname, lastName) = e;
        var (first, last, salary) = e;

Be careful, it goes without saying that there is a big risk of ambiguity. We can have extended the class Person several times with a deconstruct that has the same number of parameters.

How does it work?

Everyone who used tuples knows how it works: tuples are anonymous classes generated during compilation. However, historically, anonymous types allowed calling properties by name, not tuples. This was due to the internal structure of the code generated by the CLR.

An anonymous class creates a class by property so that it can be inferred without error, for example, the following anonymous code:

var employee = new { First = "John", Last = "Doe", Salary = 10};

translated from a compiler point of view by:

By creating a class by property, anonymous types allow access directly to named properties, with the constraints that they are familiar with. We are therefore curious to find out if it is the same for the Tuples. With the help of Visual Studio ((Debug> Windows> Disassembly) one realizes that our GetEmployeeInfo method behaves like this:

Tuples types with named properties are actually the same as Tuples types without named properties. The only addition is the ability to use more meaningful names than Item1, Item2 etc.

(string First1, string Last1) person1 = ("Jon", "Doe");
(string First2, string Last2) person2 = ("Jon", "Doe");
person1 = person2;

Conclusion

Tuple values are real syntactic sugars that provide a lot of simplicity and are easy to use. They can sometimes replace anonymous types and classes. However, for efficiency, tuples should be used only if the need is local. If the data to be grouped is used in several parts of the code, it is more appropriate to encapsulate them in a class or structure.