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.