-->

什么是C#中使用产量返回迭代器的目的/优势?(What is the purpose/advanta

2019-06-25 04:09发布

所有我见过使用的例子yield return x; 一个C#方法中可以以同样的方式由刚刚返回整个列表来完成。 在这种情况下,有没有使用任何利益或好处yield return语法与返回列表?

此外,在什么样的情况下会yield return使用,你不能只返回完整列表?

Answer 1:

但是,如果你正在建立自己的东西的集合?

通常,迭代器可以用来生成懒惰对象序列 。 例如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;
}

这个例子打印素数小于10000,您可以轻松地改变它打印不到一万个号码,而完全不触及素数生成算法。 在这个例子中,因为序列是无限的,消费者甚至不知道它从一开始就多少项目要你无法返回所有质数的列表。



Answer 2:

这里的精细答案提示的好处yield return是, 你并不需要创建一个列表 ; 列表可以是昂贵的。 (另外,一段时间后,你会发现他们笨重和不雅。)

但是,如果你没有一个列表?

yield return允许您遍历数据结构 (不一定是列表)在许多方面。 例如,如果你的对象是一棵树,你可以遍历之前或之后的顺序节点而不产生其他列表或改变底层数据结构。

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


Answer 3:

懒惰计算/延迟执行

直到你实际调用该特定结果的“产量回归”迭代块不会执行任何代码。 这意味着它们也可以有效地链接在一起。 小调查:假设“readlines方法()”函数读取一个文本文件中的所有行和使用迭代器块,多少次将在该文件下面的代码迭代实现?

var query = 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循环。

关注点分离

再次使用假想ReadLines()函数从上面,我们现在可以很容易地从该过滤掉从实际解析结果的代码未需要行的代码读出文件中的代码分开。 这第一个,尤其是非常可重复使用。

无限列表

见我回答这个问题的一个很好的例子:
C#斐波那契函数返回错误

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

改进了语义

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

如果你无法看到图像,它显示了相同的代码的两个版本,背景亮点不同的关注点。 LINQ的代码具有均能分组的颜色,而传统的命令性代码有混杂的颜色。 笔者认为(我同意),这一结果是典型的使用LINQ VS使用命令式的代码......这LINQ做一个更好的工作的组织代码有部分之间有更好的流动性。


1我相信这是原始来源: https://twitter.com/mariofusco/status/571999216039542784 。 另外请注意,这个代码是Java,但C#是相似的。



Answer 4:

有时你需要返回序列只是过大,以适应在内存中。 例如,大约3个月前,我在MS SLQ数据库之间的数据迁移项目的一部分。 数据以XML格式导出。 产量的回报竟然是与XmlReader中非常有用。 它使编程更容易比较。 例如,假设一个文件有1000个客户元素-如果你只是读取这个文件到内存中,这将要求所有的人都在内存中存储的同时,即使他们被依次处理。 所以,你可以使用迭代器,以遍历集合一个接一个。 在这种情况下,你必须花只是记忆的一个元素。

事实证明,使用的XmlReader为我们的项目是为了使应用程序工作的唯一办法-它的工作很长一段时间,但至少它没有挂整个系统并没有提高OutOfMemoryException异常 。 当然,你可以使用的XmlReader工作,而产量迭代器。 但是,迭代器让我的生活变得更轻松(我不会写输入代码,以便迅速,没有烦恼)。 关注此页面才能看到,产量迭代器如何用于解决实际问题(不只是科学与无限的序列)。



Answer 5:

在玩具/演示场景,没有很大的差异。 但也有地方收益迭代器是很有用的情况下 - 有时,整个列表不可用(例如流),或在清单计算昂贵,不太可能需要的全部。



Answer 6:

如果整个列表是巨大的,它可能会吃大量的内存只是坐在那里,而随着产量只与你所需要的发挥,当你需要它,不管有多少项目有。



Answer 7:

看看对埃里克白的博客讨论(优秀博客的方式) 懒惰与渴望评价 。



Answer 8:

使用yield return ,你可以在项目迭代而不必建立一个列表。 如果你不需要的列表,但要遍历一些项目集它可以更容易编写

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

foreach (var foo in AllFoos) {
    if (some case where we do want to operate on foo) {
        operate on foo
    } else if (another case) {
        operate on foo
    }
}

你可以把所有的逻辑,以确定您是否希望使用收益的回报,你foreach循环可以更简洁的富来操作你的方法内。



Answer 9:

这里是我以前接受的答案完全一样的问题:

加入屈服关键字值?

看迭代方法,另一种方法是,他们这样做转弯的算法“内而外”的辛勤工作。 考虑一个解析器。 它拉文本从流,会在它的模式,并生成所述内容的高级逻辑描述。

现在,我可以做这个简单的自己是一个被采取SAX方法,其中我有一个回调接口,我通知每当我找到下一个片模式的解析器作者。 因此,在SAX的情况下,每个我找到一个元素的开始时间,我称之为beginElement方法,等等。

但是,这为我的用户使用带来麻烦。 他们必须实现处理器接口,所以他们必须编写响应回调方法的状态机类。 这是很难得到正确,所以最简单的办法是使用股票实现,构建一个DOM树,然后他们将有能够走树上的便利。 不好 - 但随后整个结构被在内存中缓冲的。

但是,怎么样,而不是我写我的解析器作为一个迭代的方法?

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...
}

这将是没有任何难度比回调接口的方法来写-刚刚获得回报从我的派生的对象LanguageElement基类,而不是调用的回调方法。

用户现在可以使用的foreach通过我的解析器的输出循环,使他们获得了非常方便的命令式编程接口。

其结果是,一个自定义的API两侧看起来像他们在控制 ,因此更容易编写和理解。



Answer 10:

使用收益的根本原因是它自己生成/返回一个列表。 我们可以用返回的列表进行进一步迭代。



文章来源: What is the purpose/advantage of using yield return iterators in C#?