【问题标题】:Generators in C#?C# 中的生成器?
【发布时间】:2017-03-31 16:42:26
【问题描述】:

在 javascript 中,我可以创建一个行为如下的生成器:

function* idMaker(){
  var index = 0;
  while(true)
    yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2

等效的 C# 会是什么样子?

我想知道这是否可行:

static System.Collections.Generic.IEnumerable<int> MakeId()
{
  int index = 0;
  while (true)
    yield return index++;
}

但就我目前对 C# 的了解而言,上述内容无法按我的预期工作,而是无限循环。

【问题讨论】:

  • C# 版本像函数一样返回,所以while (true) 将锁定您的应用程序,直到您的 RAM 用完为止。
  • 为什么它的行为不符合您的意图?
  • yield 关键字使生成器惰性求值,因此 while 循环不断被中断并且不会永远运行。
  • “不知道这能不能用”好吧,试试看。
  • 看到它的工作:ideone.com/PVvkww

标签: c# iterator generator enumerable


【解决方案1】:

好吧,与您的代码等效的C# 是:

static void Main()
{
    var enumerator = MakeId().GetEnumerator();

    enumerator.MoveNext();
    Console.WriteLine(enumerator.Current); // 0    
    enumerator.MoveNext();
    Console.WriteLine(enumerator.Current); // 1    
    enumerator.MoveNext();
    Console.WriteLine(enumerator.Current); // 2

}

static IEnumerable<int> MakeId()
{
  int index = 0;
  while (true)
    yield return index++;
}

GetEnumerator 返回一个遍历集合的枚举器。

【讨论】:

  • index++ 更改为++index 使枚举器以1 开头。您通过在第一个 MoveNext() 之前检查 Current 来掩盖它,这是无效的。
【解决方案2】:

您可以通过以下方式对您的 MakeId 执行相同操作:

using (var gen = MakeId().GetEnumerator()) {
    gen.MoveNext();
    Console.WriteLine(gen.Current); // 0
    gen.MoveNext();
    Console.WriteLine(gen.Current); // 1
    gen.MoveNext();
    Console.WriteLine(gen.Current); // 2
}

如果你不喜欢一直调用MoveNext,你可以写扩展方法:

public static class Extensions {
    public static T NextValue<T>(this IEnumerator<T> enumerator) {
        enumerator.MoveNext();
        return enumerator.Current;
    }
}

然后就变成了

using (var gen = MakeId().GetEnumerator()) {                
    Console.WriteLine(gen.NextValue()); // 0                
    Console.WriteLine(gen.NextValue()); // 1                
    Console.WriteLine(gen.NextValue()); // 2
}

【讨论】:

  • 这里的第一个例子,基本上就是 foreach 所做的。
  • 是的,但是 OP 似乎想在没有 foreach 的情况下这样做,假设我需要一个 id,然后做某事,然后需要第二个 id,等等。或者想在某个字段中存储生成器。
  • 我只是指出来。学习 C# 的人可能不知道。
  • 但是我写的函数还是挂了吧?因为无限循环?所以,这在我的理解中仍然不起作用。
  • @theonlygusti 不,它不会挂起。它是迭代器,所以(简单来说)当你调用 MoveNext 时,它会将代码执行到下一个 yield return 语句,然后返回。当然要做到这一点,您的函数在内部被重写为状态机。基本上和你的javascript例子一样,它不会死循环,对吧?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-10
  • 2010-12-10
  • 2011-04-05
  • 2015-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多