"Yield" 什么时候真正需要使用?

12

Justin Etheredge在TekPub的Mastering LINQ系列中非常好地解释了它。http://tekpub.com/preview/linq - jessegavin
4个回答

25

当您需要延迟执行时。

在大多数情况下,这是有意义的,而另一种选择是构建一个临时集合。

考虑以下场景:我有一个整数列表,想要列出它们的平方。

我可以这样做:

public static IEnumerable<int> Squares(this IEnumerable<int> numbers) {
    List<int> squares = new List<int>();
    foreach (int number in numbers)
        squares.Add(number * number);

    return squares;
}

然后我可以对它们的平方进行求和,求平均值,找到最大值等操作。

但我实际上不需要为此目的填充一个全新的List<int>。我可以使用yield枚举初始列表并逐个返回它们的平方:

public static IEnumerable<int> Squares(this IEnumerable<int> numbers) {
    foreach (int number in numbers)
        yield return number * number;
}

这实际上会产生影响,但直到你开始处理非常大的集合时,才会变得明显,因为填充临时集合会非常浪费。

例如,假设我想找到某个阈值以上的第一个平方数。我可以这样做:

IEnumerable<int> numbers = GetLotsOfNumbers();
var squares = numbers.Squares();
int firstBigSquare = squares
    .Where(x => x >= 1000)
    .FirstOrDefault();

但是,如果我的Squares方法在返回之前填充了整个List<int>,那么上面的代码可能会做比必要更多的工作。


2
写得很好的例子。+1,解释为什么差异可能不明显。 - Paddy


8

3
+1 我觉得我总是太懒了... (翻译:I think I'm always too lazy...) - Adam Driscoll
1
需要注意的是,IEnumerator和IEnumerable之间存在差异。这两个词的拼写相似,以至于我曾经因为阅读了我认为是同一件事情的不同定义而感到困惑。 - Davy8
@Davy8 非常正确!许多人对它们之间的区别感到困惑。IEnumerator 包含了 yield 背后的所有逻辑;IEnumerable 只是提供对 IEnumerator 的访问。 - Rex M
IEnumerator能否替代yield功能? - user360455
1
@NewB 这就是我所说的IEnumerator和IEnumerable之间混淆的含义。IEnumerable是人们通常使用的,它是一个有序的值集合,可以使用IEnumerator潜在地具有无限长度。IEnumerator告诉foreach循环如何获取指定的IEnumerable中的下一个值。 - Davy8
显示剩余3条评论

1

请参阅本文

Yield作为返回占位符 - 它是一个非局部goto返回点,它保留了方法的环境,并允许代码“跳回”。 与将委托传递到方法中以允许您在另一个方法中注入特定逻辑(有点倒置)类似,闭包允许您在更通用的方法“周围”执行不同类型的工作,从而使您可以保持代码小型化,模块化和可重用性。

这可能会导致更高效的代码。 而不是实例化一个非常大的集合,可能可以允许按顺序对单个对象进行操作(并且在每个操作后将其丢弃)。 我想你可以构建一些情况,其中一个简单的迭代器将非常难以构建。


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