blog post image
Andrew Lock avatar

Andrew Lock

~4 min read

Deconstructors for non-tuple types in C# 7.0

As well as finally seeing the RTM of the .NET Core tooling, Visual Studio 2017 brought a whole host of new things to the table. Among these is C# 7.0, which introduces a number of new features to the language.

Many of these features are essentially syntactic sugar over things that were already possible, but were harder work or more cumbersome in earlier versions of the language. Tuples feels like one of these features that I'm going to end up using quite a lot.

Deconstructing tuples

Often you'll find that you want to return more than one value from a method. There's a number of ways you can achieve this currently (out parameters, System.Tuple, custom class) but none of them are particularly smooth. If you really are just returning two pieces of data, without any associated behaviour, then the new tuples added in C# 7 are a great fit.

I won't go into much detail on tuples here, so I suggest you checkout one of the many recent articles introducing the feature if they're new to you. I'm just going to look at one of the associated features of tuples - the ability to deconstruct them.

In the following example, the method GetUser() returns a tuple consisting of an integer and a string:

(int id, string name) GetUser()
{
    return (123, "andrewlock");
}

If I call this method from my code, I can access the id and name values by name - so much cleaner than out parameters or the Item1, Item2 of System.Tuple.

Intellisense

Another feature is the ability to automatically deconstruct the tuple values into separate variables. So for example, I could do:

(var userId, var username) = GetUser();
Console.WriteLine($"The user with id {userId} is {username}");

This creates two variables, an integer called userId and a string called username. The tuple has been automatically deconstructed into these two variables.

Deconstructing non-tuples

This feature is great, but it is actually not limited to just tuples - you can add deconstructors to all your classes!

The following example shows a User class with a deconstructor that returns the FirstName and LastName properties:

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }

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

With this in place I can deconstruct any User object:

var user = new User
{
    FirstName = "Joe",
    LastName = "Bloggs",
    Email = "[email protected]",
    Age = 23
};

(var firstName, var lastName) = user;

Console.WriteLine($"The user's name is {firstName} {lastName}");
// The user's name is Joe Bloggs

We are creating a User object, and then deconstructing it into the firstName and lastName variables, which are declared as part of the deconstruction (they don't have to be declared inlcline, you can use existing variables too).

To create a deconstructor, create a function of the following form:

public void Deconstruct(out T var1, ..., out T2 var2);

The values that are produced are declared as out parameters. You can have as many arguments as you like, the caller just needs to provide the correct number of variables when calling the deconstructor. You can even have multiple overloads with different numbers of parameters:

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }

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

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

The same user could be deconstructed in multiple ways, depending on the needs of the caller:

(var firstName1, var lastName1) = user;
(var firstName2, var lastName2, var age) = user;

Ambiguous overloads

One thing that might cross your mind is what happens if you have multiple overloads with the same number of parameters. In the following example I add an additional deconstructor also accepts three parameters, where the third parameter is a string rather than an int:

public partial class User
{
    // remainder of class as before

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

This code compiles, but if you try and actually deconstruct the object you'll get some red squigglies:

red squigglies

At first this seems like it's just a standard C# type inference error - there are two candidate method calls so you need to disambiguate between them by providing explicit types instead of var. However, even explicitly declaring the type won't clear this one up:

red squigglies 2

You'll still get the following error:

The call is ambiguous between the following methods or properties: 'Program.User.Deconstruct(out string, out string, out int)' and 'Program.User.Deconstruct(out string, out string, out string)'

So make sure not to overload multiple Deconstruct methods in a type with the same numbers of parameters!

Bonus: Predefined type 'System.ValueTuple`2' is not defined or imported

When you first start using tuples, you might get this confusing error:

Predefined type 'System.ValueTuple`2' is not defined or imported

But don't panic, you just need to add the System.ValueTuple NuGet package to your project, and all will be good again:

System.ValueTuple

Summary

This was just a quick look at the deconstruction feature that came in C# 7.0. For a more detailed look, check out some of the links below:

Andrew Lock | .Net Escapades
Want an email when
there's new posts?