Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.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#_Iterator_Yield Return - Fatal编程技术网

在C#中使用收益返回迭代器的目的/优点是什么?

在C#中使用收益返回迭代器的目的/优点是什么?,c#,iterator,yield-return,C#,Iterator,Yield Return,我见过的所有使用收益率x的例子也可以通过返回整个列表以同样的方式完成。在这些情况下,使用yield return语法与返回列表相比有什么好处或优势 另外,在哪些类型的场景中,如果您不能只返回完整的列表,您会使用收益返回。但是如果您自己构建一个集合呢 通常,迭代器可用于惰性地生成对象序列。例如,Enumerable.Range方法在内部没有任何类型的集合。它只会根据需要生成下一个数字。使用状态机生成延迟序列有很多用途。其中大多数都包含在函数式编程概念中 在我看来,如果您将迭代器视为枚举集合的一种方

我见过的所有使用
收益率x的例子也可以通过返回整个列表以同样的方式完成。在这些情况下,使用
yield return
语法与返回列表相比有什么好处或优势


另外,在哪些类型的场景中,如果您不能只返回完整的列表,您会使用
收益返回

但是如果您自己构建一个集合呢

通常,迭代器可用于惰性地生成对象序列。例如,
Enumerable.Range
方法在内部没有任何类型的集合。它只会根据需要生成下一个数字。使用状态机生成延迟序列有很多用途。其中大多数都包含在函数式编程概念中

在我看来,如果您将迭代器视为枚举集合的一种方式(这只是最简单的用例之一),那么您就错了。正如我所说,迭代器是返回序列的方法。序列甚至可能是无限的。无法返回无限长的列表并使用前100项。有时懒惰是不明智的返回集合与返回集合生成器(迭代器就是这样)有很大不同。这是把苹果和桔子做比较

假设示例:

static IEnumerable<int> GetPrimeNumbers() {
   for (int num = 2; ; ++num) 
       if (IsPrime(num))
           yield return num;
}

static void Main() { 
   foreach (var i in GetPrimeNumbers()) 
       if (i < 10000)
           Console.WriteLine(i);
       else
           break;
}
静态IEnumerable GetPrimeNumbers(){
对于(int num=2;;++num)
if(IsPrime(num))
收益数;
}
静态void Main(){
foreach(GetPrimeNumbers()中的变量i)
如果(i<10000)
控制台写入线(i);
其他的
打破
}

本例打印小于10000的素数。您可以轻松地将其更改为打印少于一百万的数字,而无需接触素数生成算法。在本例中,您无法返回所有素数的列表,因为序列是无限的,消费者甚至不知道从一开始就需要多少项。

在玩具/演示场景中,没有太大区别。但是在某些情况下,生成迭代器是有用的-有时,整个列表不可用(例如流),或者列表计算成本很高,不太可能需要完整的列表。

如果整个列表很大,它可能会消耗大量内存,只是为了坐着不动,而对于生成,您只需要处理您需要的内容,当您需要它时,不管有多少项。

请查看Eric White的博客(顺便说一句,这是一个非常好的博客)上的讨论。

使用
收益回报
您可以迭代项目,而无需构建列表。如果您不需要该列表,但希望迭代某些项集,那么编写该列表会更容易

foreach (var foo in GetSomeFoos()) {
    operate on foo
}

您可以使用收益率返回将确定是否要对foo进行操作的所有逻辑放在方法内部,这样foreach循环可以更加简洁。

Lazy Evaluation/Deferred Execution “yield-return”迭代器块不会执行任何代码,直到您实际调用该特定结果。这意味着它们也可以有效地链接在一起。突击测验:以下代码将在文件上迭代多少次

var query = File.ReadLines(@"C:\MyFile.txt")
                            .Where(l => l.Contains("search text") )
                            .Select(l => int.Parse(l.SubString(5,8))
                            .Where(i => i > 10 );

int sum=0;
foreach (int value in query) 
{
    sum += value;
}
答案恰恰是一个,直到在
foreach
循环中向下一步。即使我有三个独立的linq操作符函数,我们仍然只循环一次文件的内容

这除了性能之外还有其他好处。例如,我可以编写一个相当简单和通用的方法来读取和预过滤日志文件一次,并在几个不同的地方使用相同的方法,每次使用都会添加不同的过滤器。因此,我在有效地重用代码的同时保持了良好的性能

无限列表 请参见我对这个问题的回答,以获得一个很好的示例:

基本上,我使用一个迭代器块来实现斐波那契序列,该迭代器块永远不会停止(至少在到达MaxInt之前不会停止),然后以安全的方式使用该实现

改进的语义和关注点分离 再次使用上面的文件示例,我们现在可以轻松地将读取文件的代码与从实际解析结果的代码中过滤出不需要的行的代码分开。特别是第一个,是非常可重用的

这是其中一件很难用散文来解释的事情,而不仅仅是用简单的视觉来解释:

如果您看不到该图像,它将显示相同代码的两个版本,并针对不同的关注点突出显示背景。linq代码将所有颜色很好地分组,而传统的命令式代码将颜色混合在一起。作者认为(我也同意)这个结果是使用linq和使用命令式代码的典型结果。。。linq能够更好地组织代码,使代码在各个部分之间具有更好的流



1我认为这是原始来源:。还要注意的是,这段代码是Java,但C#将是类似的。

这是我以前对完全相同问题的公认答案:

看待迭代器方法的另一种方式是,它们努力将算法“由内而外”地转换。考虑解析器。它从流中提取文本,查找其中的模式,并生成内容的高级逻辑描述

现在,作为解析器的作者,我可以通过采用SAX方法来简化这一过程,在SAX方法中,我有一个回调接口,每当我找到模式的下一部分时都会通知它。因此,在SAX的情况下,每次我找到元素的开头时,我都调用
beginElement
方法,依此类推

但这给我的用户带来了麻烦。他们必须实现处理程序接口,因此他们必须写
var query = File.ReadLines(@"C:\MyFile.txt")
                            .Where(l => l.Contains("search text") )
                            .Select(l => int.Parse(l.SubString(5,8))
                            .Where(i => i > 10 );

int sum=0;
foreach (int value in query) 
{
    sum += value;
}
IEnumerable<LanguageElement> Parse(Stream stream)
{
    // imperative code that pulls from the stream and occasionally 
    // does things like:

    yield return new BeginStatement("if");

    // and so on...
}
public IEnumerable<T> InOrder()
{
    foreach (T k in kids)
        foreach (T n in k.InOrder())
            yield return n;
    yield return (T) this;
}

public IEnumerable<T> PreOrder()
{
    yield return (T) this;
    foreach (T k in kids)
        foreach (T n in k.PreOrder())
            yield return n;
}