当前位置:  开发笔记 > 编程语言 > 正文

在C#中使用yield return迭代器的目的/优势是什么?

如何解决《在C#中使用yieldreturn迭代器的目的/优势是什么?》经验,为你挑选了5个好方法。

我在yield return x;C#方法中看到的所有示例都可以通过返回整个列表以相同的方式完成.在这些情况下,使用yield return语法与返回列表是否有任何好处或优势?

此外,在使用哪种类型的方案时yield return,您不能只返回完整列表?



1> Mehrdad Afsh..:

但是如果你自己建造一个系列呢?

通常,迭代器可用于延迟生成一系列对象.例如,Enumerable.Range方法内部没有任何类型的集合.它只是按需生成下一个数字.使用状态机生成这种延迟序列有很多用途.其中大多数都涵盖在函数式编程概念中.

在我看来,如果你把迭代器看作是枚举集合的一种方式(它只是最简单的用例之一),那你就走错了路.正如我所说,迭代器是返回序列的手段.序列甚至可能是无限的.没有办法返回无限长度的列表并使用前100个项目.它偷懒的时候.返回集合与返回集合生成器(迭代器是什么)有很大不同.它将苹果与橙子进行比较.

假设的例子:

static IEnumerable 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;
}

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


@Dennis:对于内存中的线性存储列表,它可能没有区别但是如果你是,例如,枚举一个10GB的文件并逐个处理每一行,那就会有所不同.
除了其他原因之外,它使您的代码更加模块化,因此您可以加载项目,处理,然后重复.此外,考虑加载项目非常昂贵,或者有很多项目(数百万说)的情况.在这些情况下,加载整个列表是不可取的.
Ye olde苏格拉底方法

2> Ray..:

这里的好答案表明,一个好处yield return你不需要创建一个列表 ; 列表可能很昂贵.(此外,过了一会儿,你会发现它们笨重而且不够优雅.)

但是如果你没有List怎么办?

yield return允许您以多种方式遍历数据结构(不一定是列表).例如,如果您的对象是树,则可以按前或后顺序遍历节点,而无需创建其他列表或更改基础数据结构.

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

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



3> Joel Coehoor..:

延迟评估/延期执行

在您实际调用该特定结果之前,"yield return"迭代器块不会执行任何代码.这意味着它们也可以有效地链接在一起.Pop测验:假设"ReadLines()"函数从文本文件中读取所有行并使用迭代器块实现,以下代码将在文件上迭代多少次?

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循环中为止.

关注点分离

再次使用foreach上面的假设函数,我们现在可以轻松地将读取文件的代码与从实际解析结果的代码中过滤掉不需要的行的代码分开.特别是第一个是非常可重复使用的.

无限的名单

请参阅我对这个问题的回答,以获得一个很好的例子:
C#fibonacci函数返回错误

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

改进的语义

这是那些东西,是更难用散文来解释一个比它究竟是谁用一个简单的视觉1:

关注的命令与功能分离

如果您看不到图像,则会显示相同代码的两个版本,并针对不同的问题提供背景突出显示.linq代码具有很好地分组的所有颜色,而传统的命令式代码具有混合的颜色.作者认为(并且我同意)这个结果是使用linq与使用命令式代码的典型结果...... linq在组织代码方面做得更好,以便在各个部分之间获得更好的流程.


1我相信这是最初的来源:https: //twitter.com/mariofusco/status/571999216039542784.另请注意,此代码是Java,但C#类似.



4> SPIRiT_1984..:

有时您需要返回的序列太大而无法放入内存中.例如,大约3个月前,我参加了一个MS SLQ数据库之间的数据迁移项目.数据以XML格式导出.对于XmlReader,收益率回报非常有用.它使编程变得更加容易.例如,假设一个文件有1000个Customer元素 - 如果您只是将此文件读入内存,则需要将所有文件同时存储在内存中,即使它们是按顺序处理的.因此,您可以使用迭代器逐个遍历集合.在这种情况下,你必须花费一个元素的内存.

事实证明,对我们的项目使用XmlReader是使应用程序工作的唯一方法 - 它工作了很长时间,但至少它没有挂起整个系统并且没有引发OutOfMemoryException.当然,您可以使用XmlReader而不使用yield迭代器.但是迭代器使我的生活变得更加轻松(我不会那么快地编写导入代码而没有麻烦).观看此页面以了解如何使用yield迭代器来解决实际问题(不仅仅是无限序列的科学).



5> Dan Davies B..:

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

推荐阅读
135369一生真爱_890
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有