列出的枚举器在列入列表时不起作用。例如,如果有两个列表(两个枚举器)
public void test()
{
var firstList = new List<int>() { 1, 2, 3 };
var secondList = new List<int>() { 4, 5, 6 };
var lists = new List<List<int>>();
lists.Add(firstList);
lists.Add(secondList);
// Not working
var iterators = lists.Select(x => x.GetEnumerator()).ToList();
iterators.ForEach(x => x.MoveNext());
iterators.ForEach(x => Console.WriteLine(x.Current));
// Working
var firstIterator = iterators[0];
var secondIterator = iterators[1];
firstIterator.MoveNext(); secondIterator.MoveNext();
Console.WriteLine(firstIterator.Current);
Console.WriteLine(secondIterator.Current);
}
第一部分不起作用,它打印0 0
,而第二部分正在工作,它打印1 4
。
我不明白第一部分的错误是什么,以及如何解决。
答案 0 :(得分:6)
这是因为List<T>.GetEnumerator
方法返回List<T>.Enumerator
可变结构。
当您将其分配给变量并调用MoveNext
和Current
时,它会起作用。但是当传递给委托时,它会按值传递,因此委托会收到一份副本,因此调用MoveNext
不会影响原始struct
Current
。
可变结构的副作用之一。
如果你改变了
x => x.GetEnumerator()
到
x => (IEnumerator<int>)x.GetEnumerator()
或
x => x.AsEnumerable().GetEnumerator()
两个片段都可以使用,因为现在iterators
将包含已返回结构的盒装引用。
答案 1 :(得分:1)
因为你在不工作的部分中运行了2个单独的.ForEach()
。在1 foreach(MoveNext()
,然后打印)中执行两个操作。 MoveNext
将不会记住.Foreach()
以获得良好的解释:Go to this answer
这:
iterators.ForEach(x => x.MoveNext());
iterators.ForEach(x => Console.WriteLine(x.Current));
成为:
iterators.ForEach(x => { x.MoveNext(); Console.WriteLine(x.Current); });
答案 2 :(得分:1)
List&#39>的枚举器实现为值类型(source code):
iterators[0].GetType().IsValueType // true
这意味着 - 迭代器在将它们传递给方法时按值传递(即传递迭代器的副本而不是传递对迭代器实例的引用)。 ForEach
方法只是在循环中执行一个动作:
iterators.ForEach(copy => copy.MoveNext());
Action delegate是一个方法,因此您将每个迭代器的副本传递给此方法。所以原始迭代器保持不变。当你第二次调用ForEach
时,原始未触动过的迭代器的副本会在那里传递:
iterators.ForEach(newCopy => Console.WriteLine(newCopy.Current));
但原始迭代器处于初始状态(在第一项之前),这就是当您读取Current
值时看到整数类型的默认值的原因。
您可以使用迭代器的单个副本:
iterators.ForEach(copy => { copy.MoveNext(); Console.WriteLine(copy.Current); });
或通过引用传递它的框值类型(请注意iterators
列表的类型将更改):
var iterators = lists.Select(x => (IEnumerable<int>)x.GetEnumerator()).ToList();