Adding Tuple Support to .NET Classes in C#

Edit: Updated to improve clarity (thanks to Paulo in the comments for helping to improve his article).

Tuples in C# are objects that can be created with a specific syntax. You don’t have to declare tuple types first like you do with classes for example, they can instead be created using a lightweight C# syntax.

A tuple is a object that holds a number of arbitrary data items and which has no custom behaviour. In contrast, a class or struct can have both data and custom behaviour.

For example the following creates a tuple with 2 string values:

(string, string) names = ("Sarah", "Smith");
Console.WriteLine($"First name: '{names.Item1}' Last name: '{names.Item2}'");

This code produces the output: First name: 'Sarah' Last name: 'Smith'

In the preceding code, the items inside the tuple don’t have names so they are referred to as Item1 and Item2 but you could also name the items, for example:

(string firstName, string lastName) names = ("Sarah", "Smith");
Console.WriteLine($"First name: '{names.firstName}' Last name: '{names.lastName}'");

If you had a rich Person class that had both data and behaviour, you could also add support for tuple-like deconstruction and unpackaging of a Person instance into variables just like you would do with a tuple instance.

Consider the following class:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int AgeInYears { get; set; }
    public string FavoriteColor { get; set; }
    
    // methods etc.
}

We could create a tuple as before containing the first and last name as follows:

var sarah = new Person
{
    FirstName = "Sarah",
    LastName = "Smith",
    AgeInYears = 42,
    FavoriteColor = "red"
};

(string firstName, string lastName) names = (sarah.FirstName, sarah.LastName);
Console.WriteLine($"First name: '{names.firstName}' Last name: '{names.lastName}'");

This is however a little clunky, we can modify the Person class to provide support for a Person to have tuple-like deconstruction and unpacking semantics. To do this a public void method called Deconstruct can be added, for example:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int AgeInYears { get; set; }
    public string FavoriteColor { get; set; }

    // methods etc.

    public void Deconstruct(out string firstName, out string lastName)
    {
        firstName = FirstName;
        lastName = LastName;
    }
}

Now the code could be changed to:

var (firstName, lastName) = sarah;
Console.WriteLine($"First name: '{firstName}' Last name: '{lastName}'");

You could also add this deconstruction/unpackaging support to a class you can’t change by declaring an extension method such as:

static class PersonExtensions
{
    public static void Deconstruct(this Person person, out string firstName, out string lastName)
    {
        firstName = person.FirstName;
        lastName = person.LastName;
    }
}

Or as another example, you could add tuple-like deconstruction & unpackaging support for the .NET String type:

static class StringExtensions
{
    public static void Deconstruct(this string s, out string original, out string upper, out string lower, out int length)
    {
        original = s;
        upper = s.ToUpperInvariant();
        lower = s.ToLowerInvariant();
        length = s.Length;
    }
}

And then write:

var (original, upper, lower, length) = "The quick brown fox";
Console.WriteLine($"Original: {original}");
Console.WriteLine($"Uppercase: {upper}");
Console.WriteLine($"Lowercase: {lower}");
Console.WriteLine($"Length: {length}");

As Paulo points out in the comments there is no actual tuple instance per-se involved here, if look at the the decompiled source that Paulo links to you can see the Person has been unpackaged into multiple variables.

If you want to fill in the gaps in your C# knowledge be sure to check out my C# Tips and Traps training course from Pluralsight – get started with a free trial.

SHARE:

Comments (2) -

  • Paulo Morgado

    3/27/2020 8:41:43 AM | Reply

    There's no tuple involved in  deconstruction. What you're doing, actually, is adding the same deconstruction capabilities of tuples to other types.
    https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQMwAJboMLIN7LpGYZQAs6AsgBQCUhxBSxL6AbgIYBO61A9lwCWAc0EA7DgBsANOgCuABwUBTLrMl8A7qvXKxwgC4ALWugC86AEQAVI8vQBHOYIDGAa3QAjLlrHoAZnwAHpYA3AysmHAAnNQAJJYA8kKiEpIg6HgCIuJSAL6WtOHMkVGxCQCqSqouHADOyhl4iipcBUURrFAx8ZYAMlo19Y2ZGtpthcWlZb19eoZGTZLzxu1TxHnImyhwAGyYMOgAygZC+gCiQQZ6dYJ8YnX4nWhR++ToACLKLvd1p3IuAzUYyCOpRAAM6Dqsj4cgMEPQ2VSUhhcIRLR0iLR3UhY0xsPh4nhy30xnoJSITGmSNyknMUPWkQxPAsdQAdNY+FVWgBJMScIQcMRAjoU1h4llQjl8AbjPkCwRCkWM8UrIz09lzUlGFXbPJAA===

    • Jason Roberts

      3/28/2020 2:17:46 AM | Reply

      Thanks Paulo, I could have done a better job of of explaining in this article, I will look at updating it to make it better! - Thanks again Smile

Add comment

Loading