有人能解释一下为什么这段代码在无穷大循环中运行吗?为什么MoveNext()总是返回true?
var x = new { TempList = new List<int> { 1, 3, 6, 9 }.GetEnumerator() };
while (x.TempList.MoveNext())
{
Console.WriteLine("Hello World");
}发布于 2015-07-23 15:08:28
List.GetEnumerator()返回可变值类型(List.Enumerator)。您将该值存储在匿名类型中。
现在,让我们来看看它的作用:
while (x.TempList.MoveNext())
{
// Ignore this
}这相当于:
while (true)
{
var tmp = x.TempList;
var result = tmp.MoveNext();
if (!result)
{
break;
}
// Original loop body
}现在请注意我们在什么上调用MoveNext() --匿名类型中的值的副本。实际上,您不能更改匿名类型中的值--您所拥有的只是一个可以调用的属性,它将给您一个值的副本。
如果将代码更改为:
var x = new { TempList = (IEnumerable<int>) new List<int> { 1, 3, 6, 9 }.GetEnumerator() };..。然后,您将得到匿名类型的引用。对包含可变值的框的引用。当您对该引用调用MoveNext()时,框内的值将发生变化,因此它将执行您想要的操作。
有关非常类似的情况(同样使用List<T>.GetEnumerator())的分析,请参见my 2010 blog post "Iterate, damn you!"。
发布于 2015-07-23 22:14:53
虽然C#中的C#构造和VB.NET中的For Each循环经常与实现IEnumerable<T>的类型一起使用,但它们将接受包含返回类型提供适当的MoveNext函数和Current属性的GetEnumerator方法的任何类型。让GetEnumerator返回值类型在许多情况下将允许foreach比返回IEnumerator<T>时更有效地实现。
不幸的是,由于类型在从foreach调用时无法提供值类型枚举数,而在通过GetEnumerator方法调用时也不能提供值类型枚举器,因此List<T>的作者在性能和语义上面临着轻微的权衡。当时,由于C#不支持变量类型推断,任何使用从List<T>.GetEnumerator返回的值的代码都必须声明一个类型为IEnumerator<T>或List<T>.Enumerator的变量。使用前一种类型的代码表现得好像List<T>.Enumerator是一种引用类型,而使用后者的程序员可能会意识到它是一种结构类型。然而,当C#添加类型推断时,这个假设就不再成立了。代码很容易使用List<T>.Enumerator类型,而程序员不知道该类型的存在。
如果C#定义了一个structures属性,该属性可以用于标记不应该在只读结构上调用的方法,如果List<T>.Enumerator使用了它,那么像您这样的代码在调用MoveNext时会正确地产生编译时错误,而不是产生虚假行为。然而,我不知道有什么特别的计划来添加这样的属性。
https://stackoverflow.com/questions/31591238
复制相似问题