【问题标题】:Does Foreach Cache IEnumerable?Foreach 缓存 IEnumerable 吗?
【发布时间】:2011-07-14 08:20:37
【问题描述】:

假设SomeMethod有签名

public IEnumerable<T> SomeMethod<T>();

有什么区别

foreach (T tmp in SomeMethod<T>()) { ... }

IEnumerable<T> result = SomeMethod<T>();

foreach (T tmp in result) { ... }

换句话说,SomeMethod&lt;T&gt; 的结果是缓存在第一个语句中还是会在每次迭代时进行评估?

【问题讨论】:

    标签: c# ienumerable


    【解决方案1】:

    假设您在谈论 C#。

    foreach 翻译成这样的:

    var enumerable = SomeMethod<T>(); // Or whatever is passed to foreach
    var enumerator = enumerable.GetEnumerator();
    
    while (enumerator.MoveNext())
    {
    ...
    }
    

    因此,由于enumerable 只需要一次,因此即使您将它直接放入foreach 语句中,也只会调用一次 SomeMethod。

    【讨论】:

    • 小问题:应该有一个“使用”或“最终”块,以确保如果枚举器实现 IDisposable,它将被释放。在很多情况下,尤其是涉及迭代器的情况下,废弃的枚举器可能会导致严重的问题。
    • @supercat:你是对的,但这个例子只是为了展示 foreach 的主要概念,而不是在生产中使用。 foreach 本身将负责处理枚举数。
    • 我知道代码是为了说明而设计的,但很多人没有意识到创建实现 IDisposable 的枚举器的代码必须为了正确,确保Dispose 在枚举器上被调用。确保正确性所需要做的就是在枚举器的创建周围添加一个“使用”块。
    • @supercat:对;可以自己说,它是一个 IEnumerator,而不是 IDisposable,我为什么要尝试处置?很难看出如果你得到一个接口,它也可能是一个实现任何其他接口的对象。感谢您指出这一点。
    • @supercat:当然,“using”不能与返回 IEnumerator 的 GetEnumerator 一起使用,编译器会抱怨它无法判断它是否也是 IDisposable。所以 try-finally 必须与检查它是否真的是 IDisposable 一起使用。
    【解决方案2】:

    enumerATOR 通常是与 enumerABLE 不同类型的对象。通常,枚举器将持有对可枚举对象的引用,以及有关它在枚举过程中所处位置的一些信息。可枚举对象的目的实际上并不是提供一个项目序列,而是提供一个枚举器,该枚举器反过来提供该序列。

    在 vb.net 和 C# 中,foreach 构造的工作方式是在对象上调用一次 GetEnumerator 方法并获取它返回的对象,在从 GetEnumerator 返回的对象上重复调用 MoveNext 和 Current,最后,如果该对象实现Dispose,调用Dispose就可以了。 C# 和 vb.net 实际上都没有缓存可枚举对象,但是在调用 GetEnumerator 一次之后,它们都不需要出于任何目的使用该对象。两种语言都保留了枚举器,但除了对 MoveNext、Current 和 Dispose 的隐式调用之外,都没有提供任何使用它的方法。

    【讨论】:

      猜你喜欢
      • 2010-12-04
      • 2018-06-27
      • 2015-07-08
      • 1970-01-01
      • 2011-04-16
      • 2013-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多