Evil Code #13 : Tuple Deconstruction Assignment

It is not often that I end up writing two back to back posts on Evil Code series, and that too on the same topic. But like I said in the previous post, Tuples are really an interesting topic (if you are reading Jon Skeet’s book, then everything is interesting)

In this post, we will explore a certain behavior of tuples observed during deconstruction assignment

Consider the following code.

var person = new Person{Name="Jia",Age=4};
var personCopy = person;
(person,person.Age) = (new Person{Name="Naina"},0.2);

Console.WriteLine($"Student => Name: {person.Name}, Age: {person.Age}");
Console.WriteLine($"StudentCopy => Name: {personCopy.Name}, Age: {personCopy.Age}");

The output might surprse you at the first glance, but seems reasonable as you attempt to understand what goes behind the deconstruction assignment. Let us look at the output first before we see what happens during the deconstruction.

Student => Name: Naina, Age: 0
StudentCopy => Name: Jia, Age: 0.2

This behavior attributes itself to the 3 stage process involved in the deconstruction assignment.

Note that unlike User defined type deconstruction, which requires invocation of Deconstruct method with appropriate number of parameters.

As Microsoft has put down in the feature description,

The evaluation order can be summarized as: (1) all the side-effects on the left-hand-side, (2) all the Deconstruct invocations (if not tuple), (3) conversions (if needed), and (4) assignments.

From the above, (2 & 3) aren’t part of our concern (it is a tuple and no conversion if required as the source/destination types are the same). In the first step, the side-effect of LHS is evaluated while only in the last step the actual assignments happen.

So in this case, each of the expresion on the Left hand side, is evaluated one by one, and a storage location is reserved for it.After this, the RHS is evaluated, and any conversion to destination type is done if required.

Finally the conversion results or values from the RHS is assigned to the storage location identified in the first step.

This would mean the above code gets roughly translated to

Person person = new Person
{
    Name = "Jia",
    Age = 4.0
};
Person personCopy = person;
// LHS is evaluated and storage for person.age is reserved
Person person2 = person;
//RHS is evaluated and converted if required
Person obj = new Person
{
    Name = "Naina"
};
double num = 0.2;

// assignments
person = obj;
person2.Age = num;

Notice that during the evaluation of LHS, space reservation and evaluation is done only for person.age and not for  person from (person,person.age). This is because when assigning to a property of a variable, an extra step is required to evaluate the value of variable. This step could be skipped when the target is a variable and not property of a variable.

That explains the output we received for the earlier code.

Note: If you are a C# developer, and haven’t read Jon Skeet’s C# in Depth, do it immediately. This book is a MUST read for any C# developer.

Deconstruct and Extension Methods

In an earlier post, we explored the Deconstruct feature for Non-Tuples. It was a pretty useful feature that came along in the 7.x series . If one does have access to source, adding deconstruct is quite easy. But what about classes we do not have (source code) access to ? For example, the .Net framework classes ? Can we add a deconstruct to it ? It turns out, it is quite simple as well. Extension Methods allows us to add deconstruct to existing classes without breaking the Open Closed Principle.

For demonstration purpose, let us write a deconstruct for Point Class under the System.Drawing namespace.

public static class ExtensionMethods
{
    public static void Deconstruct(this Point source,out double x,out double y)
    {
        x = source.X;
        y = source.Y;
    }
}

void Main()
{
       Point point = new Point(100,500);
       var (x,y) = point;
       Console.WriteLine($"Point.X={x},Point.Y={y}");
}

As observed, it is quite easy to add deconstruct to your existing classes using the Extension Methods.