【问题标题】:What is the for/while equivalent of foreach?foreach 的 for/while 等价物是什么?
【发布时间】:2012-08-21 15:42:14
【问题描述】:

我有一个foreach 循环需要转换为forwhile 循环。我的循环如下所示:

foreach (Item item in Items)
{
    // some stuff
}

什么是等效的forwhile 循环?

我想我需要使用GetEnumerator 来获得IEnumerator<Item>,但我不知道如何正确使用它。 Items 不是列表,否则我会使用索引。

【问题讨论】:

  • 有什么特殊原因需要转换它吗?
  • @MassimilianoPeluso 我需要做一些在foreach 中不起作用的额外工作,所以我需要知道如何使用 GetEnumerator。
  • @KendallFrey:什么东西不感兴趣,它会与枚举器一起使用吗?您最好按照 Ryan 在他(现已删除)的答案中建议的操作,并使用计数循环。
  • 如果我们要完美替代它,它可能是如此完美,以至于它确实具有您最初想要超越的相同缺点。那么原来的缺点是什么?
  • @KendallFrey 为什么不在经典中使用 Count() 方法,然后按索引访问项目?我错过了什么吗?

标签: c# .net foreach ienumerable ienumerator


【解决方案1】:

在最简单的情况下(不处置等),您可以使用:

var enumerator = Items.GetEnumerator();// `var` to take advantage of more specific return type
while(enumerator.MoveNext())
{
  Item item = enumerator.Current;
  ...
}

有关确切规则,请查看 C# 规范 8.8.4 The foreach 语句。

形式的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
  }

}

(引自 C# 语言规范 4.0 版)

这里使用的类型是:“集合类型C,枚举类型E和元素类型T”。 EGetEnumerator 的返回类型,不一定是 IEnumerator<V>,因为 foreach 使用鸭子类型。该规范还描述了一些极端情况以及如何推断这些类型,但这些细节可能与这里无关。

在 C# 5 中,v 的声明将被移到 while 循环中以获得更直观的闭包语义。

【讨论】:

  • 这里解释了 C# 5.0 中所做的更改,以防有人错过:stackoverflow.com/questions/8898925/…
  • 您能解释一下ECT 的类型/变量吗?它们不是来自原始的foreach
  • E 是枚举器,C 是集合类型,T 是元素类型。
  • 太棒了。我了解ET,但我不明白为什么((C)(x))(x) 有任何不同。
  • 我认为这是针对没有GetEnumerator 方法,但明确实现IEnumerator<T>.GetEnumerator 的情况。 TC 的转换只是与极端情况相关的技术细节。另一方面,转换为 V 是一个清晰可见的效果:foreach 查看从 TV 的显式转换,而不仅仅是隐式转换。
【解决方案2】:

如果您要使用for 循环,这通常意味着有某种方法可以快速访问第 n 项(通常是索引器)。

for(int i = 0; i < Items.Count; i++)
{
  Item item = Items[i]; //or Items.Get(i) or whatever method is relevant.
  //...
}

如果您只是要访问迭代器,您通常只想使用foreach 循环。但是,如果您不能,这通常是有意义的模型:

using(IEnumerator<Item> iterator = Items.GetEnumerator())
while(iterator.MoveNext())
{
  Item item = iterator.Current;
  //do stuff
}

从技术上讲,您可以在 for 循环中执行此操作,但这会更难,因为该构造与此格式不匹配。如果您要讨论不能使用 foreach 循环的原因,我们可能会帮助您找到最佳解决方案,无论这是否涉及使用 for 循环。

【讨论】:

  • 正确使用枚举器需要在放弃之前调用IDisposable,除非已知枚举器的具体类型不需要清理。
  • @supercat 这就是我将迭代器包装在 using 块中的原因,它可以正确处理它...
【解决方案3】:

这是for循环中的等价物

for (IEnumerator i = Items.GetEnumerator(); i.MoveNext(); )
{
    Item item = (Item)i.Current;
    // some stuff
}

【讨论】:

  • 如果不先调用 MoveNext,访问 Current 会抛出 InvalidArgumentException。它不会为我跳过任何东西。
  • @AustinSalonen 我试过了,它奏效了。你的创造了一个额外的项目。
  • @AustinSalonen 它没有。我刚刚测试过。
  • 上面忽略了在枚举器上调用Dispose。虽然有些枚举器类型没有实现IDisposable,但应该在枚举器上调用IDisposable.Dispose,除非知道枚举器的具体类型不需要清理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-21
  • 2014-05-08
  • 2014-06-12
  • 1970-01-01
  • 2022-11-28
相关资源
最近更新 更多