1

I'm manually incrementing an enumerator and passing it to functions to handle subsets of data. Upon return from those functions, I'm finding that the enumerator has not changed state in the parent function.

Here's a simplified version of what I'm talking about.

class Program {
    static void Main(string[] args) {
        var input = new List<string>() {
            "1",
            "2",
            "escape",
            "3",
            "4",
            "return",
            "5"
        };

        foreach(var line in ParseFile(input)) {
            Console.WriteLine(line);
        }
        Console.ReadLine();
        Environment.Exit(Environment.ExitCode);
    }

    private static IEnumerable<string> ParseFile(List<string> input) {
        var enumerator = input.GetEnumerator();
        while (enumerator.MoveNext()) {
            if (enumerator.Current.Equals("escape")) {
                foreach (var line in DoStuff(enumerator)) {
                    yield return line;
                }
            } else {
                yield return enumerator.Current;
            }
        }
    }

    private static IEnumerable<string> DoStuff(IEnumerator<string> enumerator) {
        do {
            yield return enumerator.Current;
        } while (enumerator.MoveNext() && (!enumerator.Current.Equals("return")));
    }
}

What I'm seeing is that after exiting DoStuff, enumerator.Current is still "escape" and has not incremented to "return." Here's the results from the console:

1

2

escape

3

4

3

4

return

5

My question is what is causing this to happen? Is a deep copy of the enumerator being passed to the second function? If so, why does that happen? GetHashCode() is the same on both objects.

Community
  • 1
  • 1
  • 8
    [`List.Enumerator`](https://msdn.microsoft.com/en-us/library/x854yt9s.aspx) is a struct so modifications in the function occur on a copy. – Lee May 25 '17 at 16:48
  • 1
    Pass it by reference if you want to retain state (`DoStuff(ref IEnumerator enumerator)`) – Evk May 25 '17 at 16:52
  • 2
    https://stackoverflow.com/questions/3168311/why-do-bcl-collections-use-struct-enumerators-not-classes – Hans Passant May 25 '17 at 16:58
  • @Lee That's the exact piece of information I was missing. Thank you! – Andrew Miller May 25 '17 at 17:09
  • 1
    Also, `var enumerator = input.GetEnumerator();` returns an `IEumerable` (`T` is `string` in this case) which also inherits `IDisposable` and therefore should be properly `Dispose()`'d by wrapping its lifetime in a `using` statement. – Jesse C. Slicer May 25 '17 at 17:32
  • Upvoted because although it IS a duplicate, my particular search phrasing led here and not to one of the originals. – josh2112 Dec 20 '17 at 16:43

0 Answers0