C#中的foreach循环重新计算

11

如果我在C#中写了一个foreach循环:

foreach(String a in veryComplicatedFunction())
{

}

它会在每次迭代中计算veryComplicatedFunction吗?还是只会计算一次并将结果存储在某个地方?


20
你可以在veryComplicatedFunction()中添加一个Debug.WriteLine(),自己找出答案! - Chris
6个回答

44

你的问题在规范的12.9.5章节中得到了回答,其中提到:


以上步骤,若成功,则明确生成集合类型C、枚举器类型E和元素类型T。一个形如下述的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 {
        … // Dispose e
    }
}

如您所见,在这个展开式中,表达式x只被计算一次。


21

它只会计算一次


6

这要看你所说的“一次”是什么意思。如果你有以下方法:

public static IEnumerable<int> veryComplicatedFunction()
{
    List<string> l = new List<string> { "1", "2", "3" };
    foreach (var item in l)
    {
        yield return item;
    }
}

每次yield后,控制权都会返回到调用veryComplicatedFunction方法的位置。因此,如果它们共享List l,则可能会出现一些奇怪的结果。消除这种问题的可靠方法是在veryComplicatedFunction上调用ToArray扩展。


5
veryComplicatedFunction 很可能返回 IEnumerable<string>,这意味着它的计算只会执行一次,并以某种方式流式传输结果。然而,没有看到实际方法,无法保证它的作用。可以保证的是 veryComplicatedFunction 只被调用一次,foreach 遍历方法的返回值。

1

它应该只计算一次。另外,非常复杂的函数(verycomplicatedfunction())必须返回一个 IEnumerable 或者 IEnumerable<T> 或者任何拥有公共 GetEnumerator() 方法的对象。


8
实际上,你的旁注不正确。只要返回一个具有GetEnumerator方法、该方法不带参数并返回IEnumerator或其派生类的对象,它就可以与foreach语句一起使用。该类本身不需要实现IEnumerable。 - Greg Beech
天啊,你说得对。我会更正我的答案。我一直以为要使用foreach循环,底层类型必须是IEnumerable。看起来只要有公共的GetEnumerator()定义可用,编译器就可以满足。有趣... - Abhijeet Patel
一个在.NET中的鸭子类型的例子 ;-) (我认为目前还没有其他的) - Marc Wittke
这不是鸭子类型,因为编译器会检查方法的存在。这是结构子类型的一个例子,而不是更常见的命名子类型。 - Michael Donohue

0
如果每次都重新计算,可能会导致一系列问题,特别是当你的函数基于动态因素(如当前时间)时。因此,应该只计算一次。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接