yield break有什么用途?

40

可能是重复问题:
在C#中,“yield break;”有什么作用?

有人能看到“yield break”语句的用途,而这不能通过使用“break”或“return”来实现。

这个语句似乎完全没有用处。更重要的是,如果没有这个语句,“yield return X”语句可以简化为“yield X”,这样更易读。

我错过了什么吗?

6个回答

76

举个代码示例,假设您想编写一个迭代器,如果源为空或者为null,则不返回任何内容。

public IEnumerable<T> EnumerateThroughNull<T>(IEnumerable<T> source)
{
    if (source == null)
        yield break;

    foreach (T item in source)
        yield return item;
}

没有yield break,将无法在迭代器中返回一个空集合。

3
yield break 返回一个空的可枚举对象... 本质上(在您的情况下)与 return new T[0] 相同,但不会创建空数组。 - user1228
2
好的,我们可以只在source!= null时运行if语句,但是我明白了。 - Casebash

9

yield break指定方法应停止返回结果。仅使用"return"是不够的,甚至可能导致错误,因为方法的返回类型必须是IEnumerable。

我不确定为什么还需要return关键字。我最好的猜测是它有助于使语句的意图更加清晰。

如果您感兴趣,这篇文章解释了yield在幕后做了什么。

C# yield关键字背后的故事


这不是我理解的行为方式 - 'yield break' 取消整个方法,不返回任何内容。即使您已经使用 'yield return(ed)' 返回了 10K 个项目,但如果在同一方法中遇到 'yield break',则总共将得到 0 个项目。 - Dave Swersky
1
@Dave:我不这么认为...那么在迭代器块中,惰性求值怎么工作呢? - chakrit
5
@Dave,不,yield break会停止返回更多的项,但不会(也不能)取消已经返回的项。 - configurator
@Bob:仅使用“return”不会导致错误——它只是无法在迭代器块内编译。 “yield return”关键字组合是必要的,以防止破坏现有代码。 - configurator
@configurator,Miles问为什么不能单独使用return,如果允许这样做,语法可能会混淆,从而导致错误。 - Bob
@Dave,考虑一下for或foreach中的break关键字。仅仅因为你使用了break并不意味着在它之前执行的代码会被撤销。当你yield一个返回值时,方法的执行实际上是暂停的,直到请求下一项时才会恢复。 - Bob

7

yield break 和 break 执行的是完全不同的操作!看看下面

for(int i = 0; i < 10; ++i) {
   if(i > 5) { break; }
   yield return i;
}

for(int v = 2710; v < 2714; v+=2) {
   yield return v;
}

for(int s = 16; s < 147; ++s) {
   if(s == 27) { yield break; }
   else if(s > 17) { yield return s; }
}

输出将是这些值的IEnumerable: 0、1、2、3、4、5、2710、2712、18、19、20、21、22、23、24、25、26。
下面是一些没有实际用途的方法,但它说明了您可以将yield break和break结合在同一个方法中,并且它们执行两个不同的操作!Break只是跳出循环,而yield break则完全结束该方法。
我猜return没有被使用的原因是因为您在方法中没有返回自己的IEnumerable。此外,在这种迭代情况下,您是否认为yield break比return更清晰?我个人认为是这样的。

3
在迭代器块中使用return语句是非法的。此外,如果允许break语句影响迭代过程,则break语句可以在迭代器块中发挥双重作用:它可用于跳出循环或跳出开关,并可用于跳出整个迭代。 yield returnyield break是两个独立的关键字,它们似乎都是必要的。

0

你需要区分返回已存在的IEnumerable实现(例如,你将要返回一个List)和匿名迭代器。

你不能仅仅使用return或break,因为它们是可以用于其他目的的有效关键字。你也不能仅仅使用yield,因为你需要有一种方式来指定你是否正在返回枚举中的项目,或者结束枚举,因此需要使用yield return和yield break。


0

return语句用于从函数中返回一个值。

如果您有一个像这样的迭代器函数:

public IEnumerable<int> IteratorOverInt() { }

在上述函数中,一个返回语句需要返回一个填充了 IEnumerable<int> 的对象。
public IEnumerable<int> IteratorOverInt() {
    return new List<int> { 1, 2, 3, 4 };
}

但如果您正在编写迭代器函数,您将使用 yield return,因此您的代码可能如下所示:

public IEnumerable<int> IteratorOverInt() {
    for (int i = 0; i < 4; i++) {
        yield return i;
    }
}

所以yield return返回一个int,而方法签名要求IEnumerable<int>

在上述情况下,return语句会做什么?覆盖yield的结果?将任何内容连接到yield中?在上述情况下,return没有太多意义。

因此,我们需要一种在迭代器块中有意义的return方式,这就是为什么有yield break的原因。

当然,您可以使用break来实现,但是如果您有更多要执行的代码,那么如何退出函数体呢?将所有内容包装在复杂的if-else块中?我认为只使用yield break会更好。


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