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 learn a load more C# tips check out my C# Tips and Traps course today. You can even currently start watching with a Pluralsight Free Trial with Unlimited Access .

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