C#中的生成器?

4
在 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用尽。 - Manfred Radlwimmer
为什么它没有按照你的意图运行? - Evk
1
yield 关键字使生成器变成惰性求值,因此 while 循环会被中断,不会无限运行。 - Cameron MacFarland
2
“我不知道这是否有效。” 那就试试看吧。 - Dennis_E
1
查看它的运行效果:http://ideone.com/PVvkww - Dennis_E
显示剩余2条评论
2个回答

9
您可以通过以下方式使用您的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 的作用。 - Cameron MacFarland
1
是的,但 OP 似乎想要在没有 foreach 的情况下做到这一点。比如说我需要一个 ID,然后做一些事情,然后需要第二个 ID,以此类推。或者想要将生成器存储在某个字段中。 - Evk
我只是指出来而已。学习C#的人可能不知道这个。 - Cameron MacFarland
但是我写的函数仍然会挂起,对吗?因为无限循环的原因?所以,据我理解,这仍然不起作用。 - theonlygusti
@theonlygusti 不会挂起。这是迭代器,所以(简单来说)当您调用MoveNext时,它执行到下一个yield return语句的代码,然后返回。当然,为了做到这一点,您的函数在内部被重写为状态机。基本上与您的JavaScript示例相同,在那里它不会在无限循环中挂起,对吧? - Evk

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 返回一个枚举器,用于遍历集合。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - user743382

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