【问题标题】:Using IEnumerable without foreach loop在没有 foreach 循环的情况下使用 IEnumerable
【发布时间】:2011-01-15 23:30:31
【问题描述】:

我必须在这里遗漏一些简单的东西。

取以下代码:

public IEnumerable<int> getInt(){
  for(int i = 0; i < 10; i++){
   yield return i;
  }
}

我可以这样称呼它:

foreach (int j in obj.getInt()){
  //do something with j
}

如何在没有 foreach 循环的情况下使用 getInt 方法:

IEnumerable<int> iter = obj.getInt();
// do something with iter ??

谢谢。

编辑

对于那些想知道我为什么想要这个的人。我正在迭代两件事:

IEnumerator<int> iter = obj.getInt().GetEnumerator();
foreach(object x in xs){
  if (x.someCondition) continue;
  iter.MoveNext();
  int n = iter.current();
  x.someProp = n;
  etc...
}

【问题讨论】:

  • 你到底想做什么? IEnumerable&lt;T&gt; 的重点是允许迭代,foreach 循环是最有效的方法。
  • @Zakalwe,我想一次迭代两个可枚举。 foreach 正在通过第一个,如果满足条件,我需要下一个 int(只是一个例子,不是真正的 int)。
  • 这是一个非常好的问题。在 python 中它非常简单,我正在研究如何用 C# 来做,GetEnumerator 就是答案

标签: c# ienumerable yield-return


【解决方案1】:

您可以使用GetEnumerator 方法获得对Enumerator 的引用,然后您可以使用MoveNext() 方法继续前进,并使用Current 属性访问您的元素:

var enumerator = getInt().GetEnumerator();
while(enumerator.MoveNext())
{
    int n = enumerator.Current;
    Console.WriteLine(n);
}

【讨论】:

  • 对。但是用这个代替 foreach 有什么好处呢?
  • 它允许您对前几个元素进行特殊处理,或者在处理完很多元素后完全停止,等等。基本上它为您提供了更多的多功能性。
  • @Anon:你可以用foreach做同样的事情。
  • 详细说明您将如何处理枚举的某些元素,然后完全做其他事情,然后返回并使用foreach 完成其余的处理。
  • @Anon: 像这样:int i = 0; foreach(var x in y) { Process(x);我++; if (i == 5) GoOffAndDoSomethingDifferent(); }。看看那里会发生什么?你处理前几个,然后你去完全做其他事情,然后你回来完成其余的处理。 “去完全做其他事情,完成后从你离开的地方继续”是对我们通常认为是“调用方法”的操作的描述。
【解决方案2】:

我的建议:根本不要乱用枚举器。将您的问题描述为对序列的一系列操作。编写代码来表达这些操作。让序列运算符负责管理枚举数。

那么让我们看看我是否说得通。你有两个序列。假设 { 2, 3, 5, 7, 12 } 和 { "frog", "toad" }。您要执行的逻辑操作是,说“通过第一个序列。每次找到一个可被三整除的数字时,获取第二个序列中的下一个项目。对生成的 (number, amphibian) 对执行一些操作。”

轻松搞定。首先,过滤第一个序列:

var filtered = firstSequence.Where(x=>x%3 == 0);

接下来,用第二个序列压缩过滤后的序列:

var zipped = filtered.Zip(
             secondSequence, 
             (y, z)=> new {Number = x, Amphibian = y});

现在您可以遍历压缩序列并对配对做任何您想做的事情:

foreach(var pair in zipped)
    Console.WriteLine("{0} : {1}", pair.Number, pair.Amphibian);

简单易行,不用和枚举数混在一起。

【讨论】:

  • @Eric,谢谢,我喜欢这样,这非常接近我的需要。我花了更多时间在 python 中编程,这与我在那里的方式非常相似(我需要研究 C# 的功能)。不过我很好奇,枚举器的潜在陷阱是什么?
  • @Mark: (1) 人们忘记处理它们。 (2) 枚举器强调如何代码工作而不是它的用途——用枚举器阅读代码就像在你想要什么时查看钻机的描述describe 是由此产生的洞。它们有时当然是必要的,但我更愿意在更高的抽象层次上表达我的操作,而不是我必须明确地跟踪多个序列中的多个“光标”。
  • @Eric,对您的方法的一个警告是,如果这对大量内存数据起作用,则可能是通过同时手动迭代两个列表来提高性能...如果是少量数据或性能不是问题,然后我选择优雅的代码(即你的)而不是细节代码。
  • @Jason:Zip 所做的只是分配两个枚举器并运行它们。开销应该很小。请记住,序列运算符是惰性的;他们不会一下子意识到他们的结果,他们只是根据要求从源序列中提取。
  • @Eric,如果我在这里遗漏了什么,请告诉我。步骤 1. 迭代所有 Collection 1,只选择感兴趣的项目。步骤 2. 迭代所有集合 2,将其与集合 1 “连接”。 步骤 3. 对配对中的每个元素做一些事情。假设每个结果集合的大小为 N,Big O 给我们 O(N) 的操作顺序。伟大的!。精彩的!。但是,对于绝对操作数,它仍然是 3*N。这比绝对必要的要多一关。代码要简单得多,因此首选用于额外传递不会导致可察觉的性能损失的情况。
【解决方案3】:

这个怎么样?

IEnumerator<int> iter = obj.getInt();
using(iter) {
    while(iter.MoveNext()) {
        DoSomethingWith(iter.Current)
    }
}

【讨论】:

  • @itowlson,这正是我所缺少的!
【解决方案4】:

使用for循环:

for (var enumerator = getInt().GetEnumerator(); enumerator.MoveNext(); )
{
    Console.WriteLine(enumerator.Current);
}

【讨论】:

  • 你必须把枚举数放在最后
【解决方案5】:

重要的是要提到foreach 循环的职责是在实例实现IDisposable 时释放枚举器。换句话说,foreach 应该被替换为:

var enumerator = enumerable.GetEnumerator();

try
{
    while (enumerator.MoveNext())
    {
        var item = enumerator.Current;
        // do stuff
    }
}
finally
{
    var disposable = enumerator as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

【讨论】:

    【解决方案6】:

    虽然接受的答案是正确的,但请注意 IEnumerator.Current 在第一次调用 MoveNext() 之前未定义。

    如果您要遍历辅助数组,您将需要类似:

    IEnumerable<Foo> Foo() { ... }
    
    int AssignValues(List<TakesFoo> data) {
      var count = 0;
      var foo = Foo().GetEnumerator();
    
      // Step into the first element of the array;
      // doing this does not discard a value from the IEnumerator
      if (foo.MoveNext()) { 
        foreach (var x in data) {
          x.SetFoo(foo.Current);
          count += 1;
          if (!foo.MoveNext()) { 
            break;
          }
       }
    
       // Return count of assigned values
       return count;
    }
    

    【讨论】:

      【解决方案7】:

      样品,

      public static void SampleIteratorNext()
      {
          var beauty = BeautifulLadies().GetEnumerator();
      
          Console.WriteLine($"Friday with {Next(beauty)} ...");
          Console.WriteLine($"Saturday with {Next(beauty)} ...");
          Console.WriteLine($"Tusday with {Next(beauty)} ...");
      }
      
      public static IEnumerable<string> BeautifulLadies()
      {
          yield return "Scarlett";
          yield return "Alexandra";
          yield return "Alisson";
      }
      
      // emulate next() in python
      public static T Next<T>(IEnumerator<T> iterator)
      {
          iterator.MoveNext();
          return iterator.Current;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-15
        • 2011-03-13
        • 2013-02-27
        • 1970-01-01
        相关资源
        最近更新 更多