Thursday, June 1, 2023

C# Puzzle No.25 (intermediate)

Let's have list of references to objects of a custom type that have a state. Let's loop through the list and alter the internal state of each object on the list.

Will the change of the internal state be preserved after the loop ends?

A first answer should of course be yes.

Take a look at this then:

var elements =
    Enumerable.Range(0, 10)
        .Select( i => new Foo() { Bar = i });

foreach ( var e in elements )
{
    // yields 0,1,2,3,4,5,6,7,8,9 which is correct
    Console.WriteLine( e.Bar );
}

foreach ( var e in elements )
{
    e.Bar += 10;

    // yields 10, 11, ...., 19
    Console.WriteLine( e.Bar ); 
}

foreach ( var e in elements )
{
    // yields 0,1,2,3,4,5,6,7,8,9 which doesn't seem correct 
    // (original values restored)!
    Console.WriteLine( e.Bar );
    
    // expected: 10, 11, ..., 19!
}


public class Foo
{
    public int Bar { get; set; }
}

First we create a list of elements. Then we loop it to check if it's correct. Then we loop it to alter the internal state of objects.

Then we loop again and, oddly, it seems like original values are restored.

Not that obvious to spot what's wrong here, a similar issue was caught on production code. The goal is to find a simplest fix.

1 comment:

apl said...

"First we create a list of elements" - this was the intention, but this is not what was implemented. What we've got here is, in Python terms, a generator that returns a new collection of objects every time you iterate over it. I can't think of a simpler solution than to call ToList() at the end of the LINQ expression.