Enumerator.MoveNext()的奇怪行为

iva*_*ax9 35 c# enumerator infinite-loop

有人可以解释为什么这段代码在无限循环中运行?为什么总是MoveNext()回来true

var x = new { TempList = new List<int> { 1, 3, 6, 9 }.GetEnumerator() };
while (x.TempList.MoveNext())
{
  Console.WriteLine("Hello World");
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 41

List<T>.GetEnumerator()返回一个可变值类型(List<T>.Enumerator).您将该值存储在匿名类型中.

现在,我们来看看它的作用:

while (x.TempList.MoveNext())
{
    // Ignore this
}
Run Code Online (Sandbox Code Playgroud)

这相当于:

while (true)
{
    var tmp = x.TempList;
    var result = tmp.MoveNext();
    if (!result)
    {
        break;
    }

    // Original loop body
}
Run Code Online (Sandbox Code Playgroud)

现在请注意我们正在调用的MoveNext()内容 - 匿名类型的值的副本.您实际上无法更改匿名类型中的值 - 您所拥有的只是您可以调用的属性,它将为您提供值的副本.

如果您将代码更改为:

var x = new { TempList = (IEnumerable<int>) new List<int> { 1, 3, 6, 9 }.GetEnumerator() };
Run Code Online (Sandbox Code Playgroud)

...然后你最终会得到一个匿名类型的引用.对包含可变值的框的引用.当您调用MoveNext()该引用时,该框内的值将被突变,因此它将执行您想要的操作.

有关非常类似情况的分析(再次使用List<T>.GetEnumerator())请参阅我的2010年博客文章"迭代,该死的你!" .

  • 我正在看这个并且无法确定这里的哪个元素是一个值类型,其中复制/引用的区别实际上有所不同. (3认同)
  • @MasonWheeler:`TempList`的编译时类型是`List <T> .Enumerator`,它是一个值类型. (3认同)