我一直在网上搜索,但我无法找到任何关于C#中foreach
循环幕后真实情况的答案。
我知道这个问题并不真正与实际编码有关,但它困扰着我。我对OO编程很熟悉,特别是接口。我知道他们是合同,我理解IEnumerable
和IEnumerator
是如何工作的 - 或者我认为。
我一直在MSDN上阅读这篇文章: IEnumerable Interface
我理解一切都是如何设置的。我在Main
循环中有点不清楚,foreach
知道如何通过_people
中的所有值进行迭代。怎么知道这个?如何通过调用Current
来跟踪return new PeopleEnum(_people);
?
编辑:我看不出这是一个完全重复的内容。是的,它的类似理由和同样的问题被问到,但我们正在寻找不同的答案或我想要的答案在前一个问题中没有讨论过。
foreach循环,如foreach(int i in obj){...}有点等于
...“有点”不是我正在寻找的答案。
答案 0 :(得分:22)
我建议您阅读C#规范的第8.8.4节,它会详细回答您的问题。为方便起见,请在此处引用:
表格的foreach声明
foreach (V v in x) embedded-statement
然后扩展为:
{
E e = ((C)(x)).GetEnumerator();
try
{
V v;
while (e.MoveNext())
{
v = (V)(T)e.Current;
embedded-statement
}
}
finally
{
code to Dispose e if necessary
}
}
类型E,C,V和T是由语义分析器推导出的枚举器,集合,循环变量和集合元素类型;有关详细信息,请参阅规范。
所以你去吧。 “foreach”只是编写调用MoveNext的“while”循环的一种更方便的方法,直到MoveNext返回false。
一些微妙的事情:
这不一定是生成的代码;所需要的只是我们生成产生相同结果的代码。例如,如果你在一个数组或一个字符串上“foreach”,我们只生成一个“for”循环(或多循环,在多d数组的情况下)索引数组或字符串的字符,而不是采取以分配调查员为代价。
如果枚举数是值类型,那么处置代码可能会也可能不会选择在处理枚举器之前对其进行包装。不要依赖那种方式。 (有关相关问题,请参阅http://blogs.msdn.com/b/ericlippert/archive/2011/03/14/to-box-or-not-to-box-that-is-the-question.aspx。)
同样,如果上面自动插入的强制转换被确定为标识转换,那么即使这样做通常会导致复制值类型,也可能会删除强制转换。
C#的未来版本可能会将循环变量v
的声明放在 while循环体中;这将防止在Stack Overflow上每天报告一次的常见“修改后的闭包”错误。 [更新: This change has indeed been implemented in C# 5。]
答案 1 :(得分:3)
像这样考虑:
var enumerator = <YourEnumerableHere>.GetEnumerator();
while(enumerator.MoveNext())
<YourForEachMethodBodyHere>
Ergo,当它调用GetEnumerator
时,您会将结果返回PeopleEnum
。每次调用MoveNext
方法时,PeopleEnum
的位置都会增加1,迭代列表。
当位置到达结尾时,MoveNext
调用返回false,循环结束。
答案 2 :(得分:0)
它知道迭代IEnumerable
,因为这是语言设计者决定foreach
应该做的事情。 foreach
为您提供了一种自行循环IEnumerable
的简便方式。
IEnumerator
类正在定义“获取下一个元素”的含义。如果你愿意,可以让IEnumerator
返回其他所有元素,但是大多数人只想按顺序遍历每个元素。
我不确定我是否回答了你的问题。如果没有,请告诉我,我会回复。