2

I am trying to understand what goes wrong in the following piece of code.

    var signals = new List<List<double>>
    {
        new List<double> {1, 2, 3},
        new List<double> {2, 3, 4},
    };

    var enumerators = signals.Select(l => l.GetEnumerator()).ToList();

    if (enumerators.All(enumerator => enumerator.MoveNext()))
    {
        var cummulative = enumerators.Sum(enumerator => enumerator.Current);
    }

Why are both enumerators pointing to the current value 0? I would expect both would point to the first number in the list, which would be 1 and 2 respectively.

Each time I access the enumerator via linq it seems to restart. Why?

bas
  • 13,550
  • 20
  • 69
  • 146
  • Because the enumerator is before the first position in the collection and current is undefined. – Trevor Mar 17 '20 at 13:24
  • @Çöđěxěŕ why is it undefined? I called the MoveNext in the if statement – bas Mar 17 '20 at 13:31
  • This is what happens when you use linq to mutate data. Things never go as you'd expect. Don't do it, linq isn't meant to be used like this. – InBetween Mar 17 '20 at 13:35
  • @InBetween whoa! That's quite a statement too :). How is linq meant to be used? PS: I am not mutating anything yet, I am simply getting values through iterators. – bas Mar 17 '20 at 13:42
  • Does this answer your question? [List.All(e => e.MoveNext()) doesn't move my enumerators on](https://stackoverflow.com/questions/23537567/listienumerator-alle-e-movenext-doesnt-move-my-enumerators-on) – Lucifer Mar 17 '20 at 13:42
  • 2
    Yes, you are mutating. What do you think `e => e.MoveNext()` supposedly does? Your false expectations are based upon `All` *mutating* the enumerators and that the changes are permanent in the underlying data which is not true. – InBetween Mar 17 '20 at 13:47

2 Answers2

3

Change your code as per below

var enumerators = signals.Select(l => l.GetEnumerator() as IEnumerator<double>).ToList();

As per this SO post

It is because the enumerator of List is a struct whereas the enumerator of Array is a class.

So when you call Enumerable.All with the struct, copy of enumerator is made and passed as a parameter to Func since structs are copied by value. So e.MoveNext is called on the copy, not the original.

Community
  • 1
  • 1
Lucifer
  • 1,594
  • 2
  • 18
  • 32
1

According to List<T>.GetEnumerator

Initially, the enumerator is positioned before the first element in the collection. At this position, the Current property is undefined. Therefore, you must call the MoveNext method to advance the enumerator to the first element of the collection before reading the value of Current.

To get an expected behavior and sum the value you can write the following

var cumulative = 0d;
foreach (var enumerator in enumerators)
{
    if (enumerator.MoveNext())
        cumulative += enumerator.Current;
}

All just return the bool value, it doesn't change the source sequence or its items.

Each time I access the enumerator via linq it seems to restart. Why?

Enumerator<T> is a struct and value type, it's not a class. Every time when you modify it, you modify the copy, the element in source enumerators list remains unchanged and enumerator positioned before the first element

Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66
  • Sorry I might have asked the question unclear. In the example above, I DO call MoveNext on all enumerators. Then, when accessing the enumerator again (from what I would expect the same object) the `Current` property is zero – bas Mar 17 '20 at 13:29
  • @bas no, `All` doesn't work that way. `Any` just determines whether any element of a sequence exists or satisfies a condition; it doesn't change the collection... – Trevor Mar 17 '20 at 13:31
  • @Çöđěxěŕ any? Not using .Any – bas Mar 17 '20 at 13:32
  • Why does the `.All` just return the value? It executed the method `Enumerator.MoveNext()`. I don't understand how that effect simply never happened – bas Mar 17 '20 at 13:40
  • @bas `Enumerator` is a value type and struct, you modify the copy, which doesn't affect the element in source list – Pavel Anikhouski Mar 17 '20 at 13:41
  • 1
    Right, you and Lucifer nailed it. Thx – bas Mar 17 '20 at 13:46