Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在多次枚举期间调用了两次生成器方法_C#_Ienumerable - Fatal编程技术网

C# 在多次枚举期间调用了两次生成器方法

C# 在多次枚举期间调用了两次生成器方法,c#,ienumerable,C#,Ienumerable,我试图理解为什么以下代码的行为如此: using System; using System.Collections.Generic; using System.Linq; namespace ConsoleApplication { internal class Program { private static IEnumerable<int> Foo() { Console.WriteLine("Hello w

我试图理解为什么以下代码的行为如此:

using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication
{
    internal class Program
    {
        private static IEnumerable<int> Foo()
        {
            Console.WriteLine("Hello world!");
            for (var i = 0; i < 10; i++)
                yield return i;
        }

        public static void Main(string[] args)
        {
            var x = Foo();

            var y = x.Take(3);
            foreach (var i in y)
                Console.WriteLine(i);

            var z = x.Skip(3);
            foreach (var i in z)
                Console.WriteLine(i);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
命名空间控制台应用程序
{
内部课程计划
{
私有静态IEnumerable Foo()
{
控制台。WriteLine(“你好,世界!”);
对于(变量i=0;i<10;i++)
收益率i;
}
公共静态void Main(字符串[]args)
{
var x=Foo();
变量y=x.Take(3);
foreach(变量i在y中)
控制台写入线(i);
var z=x.Skip(3);
foreach(变量i在z中)
控制台写入线(i);
}
}
}
大体上,我得到了一个名为x的新“生成器”(请原谅python术语),然后我尝试枚举它两次。 第一次打印前3个元素时:我希望程序打印“Hello world!”,后面是从0到2的数字。 第二次使用相同的可枚举项并跳过3个以上的元素。 我希望程序打印6到9之间的数字,而不首先打印“Hello world!”

相反,程序第二次打印“Hello world!”第二次,然后从3开始枚举,直到9

我不明白:为什么调用Foo()两次

EDIT1:不是重复的。链接的“duplicate”询问编写枚举时的最佳实践,在这里我很难使用它的值

EDIT2:我接受了一个诚实地说不太好的答案,但其中包含一个链接,指向一个使我理解问题的参考。 让我困惑的是,当在IEnumerable上循环时,foreach循环实际上是如何工作的,所以我将在这里解释它以供将来参考

IEnumerable是值的序列——只要它是能够按顺序给你值的东西,那么底层的实现是不相关的。 当对其使用
foreach
循环时,它会创建一个枚举器对象,它基本上是一个跟踪循环到达位置的光标

因此,第一个循环创建了第一个枚举数,该枚举数必须启动生成器方法并打印第一个字符串

第二个循环无法提取上一个枚举数(我相信它是这样做的)。 相反,它实例化第二个枚举器,该枚举器反过来重新启动生成器,从而打印第二个字符串

对于任何阅读本文的pythonista,请注意,这与python中生成器的工作方式相反(重用iterable不会重新启动它)。

在C#中,当您在返回
IEnumerable
IEnumerable
方法(或属性)的方法或属性中使用
yield
关键字时成为一个惰性序列,它不仅返回类似于列表或数组的结构,还运行该方法,直到第一个
yield
语句,然后暂停,直到下一个元素从调用代码中“提取”(您的
Main
方法)

因此,当您编写
x=Foo()
时,您将
x
设置为一个惰性序列,该序列每次循环执行
Foo
的主体。
Foo
主体中的任何副作用将在此状态机的每次迭代中执行一次

Skip
Take
分别将一个惰性序列作为输入,然后
根据需要返回
元素,生成一个新的惰性序列。因此,当您编写
y=x.Take(3)
时,不会在那里迭代任何内容,但是当您编写
foreach
y
时,它将有效地执行
Foo
,直到第三条
语句产生
为止

跳过
源代码:

同样地,
z=x.Skip(3)
通过使用
Skip
操作组合
x
创建第三个惰性序列。此处不会立即迭代任何内容,但当您
foreach
z
时,它将执行
Foo
的全部内容,但会“抛出”前3个元素


请参阅Jon Skeet的书:

,因为它是“IEnumerable”。这意味着每次调用枚举数(您的方法)时都会调用它。 如果您不希望它被调用两次,那么您应该实现它:


var x=Foo().ToArray()

可能的重复我不同意它是重复的。这个问题询问了在编写iterable时的最佳实践;我正在试图理解为什么我的生成器会被重置,尽管我并不期望它会被重置。x不会在后续调用之间保存状态,您总是会得到Foo()生成的完整枚举(包括副作用)。@HelmutD,谢谢,这就是原因。我来自python,在python中,保持其状态的是生成器本身。。。但是我的状态机没有被处理。我仍然有一个有效的参考资料。我没有做
z=Foo().Skip(3)
,我正在做
z=x.Skip(3)
,x已经被迭代过了。@JamesFaix解释是错误的,调用迭代的是
foreach
,而不是
Skip
Take
你认为
Skip
Take
是如何实现的?使用延迟执行-删除foreaches,并且不会在allExactly处调用Foo-因此,“当您写入y=x.Take(3)时,Foo的主体被迭代最多3个元素,然后状态机被释放”是不正确的