Linq Deferred Loading impact on atomic operators

When working with Atomic Operators and deferred operations provided by the Linq extensions, it is important to understand the behaviour of the program. Consider the following code:

int i = 10;

var q = (from m in Enumerable.Range(1, 5) select ++i + i++); 

foreach (var n in q) 
{
    Console.WriteLine("i = {0}; n = {1}; sum = {2}", i, n, ++i + i++);
}

What values would one expect to see on the console? Try to reason it out before coding a test project.

Show Output

Explanation

Before explaining the logic operated by the code above. Let’s first understand how the prefix and postfix atomic operators differ. Consider the following simple code

int i = 1;
Console.WriteLine("i = {0}; ++i + i++ = {1}; i = {2}", i, ++i + i++, i);

The output of this simple code is

  i = 1; ++i + i++ = 4; i = 3

Step by step evaluation: 

  1. The value of i is initialised to 1. 
  2. When the operation ++i is encountered, the value of i is incremented before the value of i is used. That is when the atomic increment was converted to get executed the value of 1 was loaded in the registry and immediately incremented by 1 to the value 2 and stored back in the variable i.
  3. The next operator encountered is the plus (summation) operator so the next variable is read. 
  4. Here comes the funny part. Although we have the expression i++, for the compiler this is evaluated as i + 1. So the next variable is just i and not i + 1. So the value of i, is still 2 for the summation. 
  5. The summation is then calculated leading to the value of 4 
  6. After the value of i, was reused for the summation the value in i gets incremented by 1. Therefore the final value of i is 3.

For more information on the prefix and postfix increment operators, refer to https://msdn.microsoft.com/en-us/library/36x43w8w.aspx 

Back to the first piece of code, with the understanding of how the prefix and postfix operators is understood it can be concluded that the value of q is a list with the values [22, 26, 30, 34. 38]. 

But wait… 

According to the output of the program the value of q is a list with the values [26, 34,42,50,58]. What is going in here… 

Here is where linq deferred loading played it’s role and changed the traditional behaviour.  When the linq statement was declared in line 2, the variable q was assigned a reference to the query only. That is the linq statement didn’t execute. Next the foreach got into play. In SQL linq the foreach would have loaded the records from the database into memory. But in the case of the Enumerable linq expression the computation of the linq expression is computed on each iteration. In other words, the loop to fill in the list q, is calculated one value at a time. Therefore, the value of i at each iteration is impacted by the Console.WriteLine statement. Complex right, so let’s do a step by step execution to see what was going on. 

  1. The variable i is initialised to value 10 
  2. The variable q is assigned an anonymous function that will yield a new value of ‘++i + i++’ on each execution 
  3. The foreach calls the anonymous function to calculate the first value 
    1. As the value of i is 10. The evaluation of ++i + i++ returns the value 11 + 11 = 22 and sets the value of i to 12 
  4. Then the Console.WriteLine function is called, but first the expression ++i + i++ needs to be called to pass it’s value to the console. The evaluation will now use 12 for the initial value of i, and will give the value of 26 and set the value of i to 14. 
  5. So the Console will display, i = 12; n = 22; sum = 26 
  6. For the second loop, the anonymous function is called again this time with the value of i as 14. This assigns the variable n the value of 30 and i to 16. 
  7. Again the Console function is called, which triggers again the calculation expression on i. This will get i to be incremented. 
  8. This will repeat for the remaining 3 iterations. 

That is some behaviour.

Although the scenario used in this article is never or rarely encountered in real programs, it was intended to show the difference between the prefix and postfix operators and how Linq defered loading impact the variables.