Search code examples
c#.netienumerable

IEnumerable<T>'s value changes every time I try to inspect it in debug mode in VS2022


In 4 years of C# soft engineering I have never encountered something like this. I thought Debugger's value inspect feature should not change inspected value's raw value, but I guess Debugger doesn't agree with that.

So, I have a rough idea why this might happen, and even found a similar case in Microsoft's C# forum, but not very sure of it. Also, when I returned newMsg and tried to call .ToArray() multiple times in a row, it gave different values to each call. My theory is, that every time I try to get IEnumerable's value, it does a recursive LINQ command, that in the process changes its datasource.

public IEnumerable<byte> Shift(IEnumerable<byte> msg)
{
    var parts = msg.Chunk(blockLength);
    var keyEnumerator = shifter.GetEnumerator();
    var newMsg = parts.SelectMany(part =>
    {
        if (!keyEnumerator.MoveNext())
        {
            keyEnumerator = shifter.GetEnumerator();
            //keyEnumerator.Reset();
            keyEnumerator.MoveNext();
        }
        byte shift = (byte)(keyEnumerator.Current % blockLength);
        var res = part.TakeLast(blockLength - shift).Concat(part.Take(shift));
        //Debug.Write(string.Join('-', res.Select(i => $"{i:X2}")) + "-");
        return res;
    });

    return TextManipualtion.UnPadByteIEnumerable(newMsg);
}

Can someone explain if this is a real bug or is it intended to be like this.

One of the reasons of this problem may also be that without Reset() enumerator cant work properly but there is a code under shows that you can reset enumerator by calling GetEnumerator() again.

IEnumerable<int> ints = Enumerable.Range(0, 5);
var atro = ints.GetEnumerator();
for (int i = 0; i < 2; i++)
{
    atro.MoveNext();
    Console.WriteLine(atro.Current);
}

atro = ints.GetEnumerator();
for (int i = 0; i < 4; i++)
{
    atro.MoveNext();
    Console.WriteLine(atro.Current);
}

//Output
//0
//1
//0
//1
//2
//3

Solution

  • The debugger has to actually enumerate the newMsg, which causes your code in SelectMany to execute. The keyEnumerator keeps being enumerated, never 'reset' between debugger's enumerations of newMsg. That may lead to different results each time.

    That's also why the user interface tells you that “Expanding the Results View will enumerate IEnumerable”. Unless it is a static collection, which is not in your case, the results will differ each time.