关于.NET:在C中”yield break”是什么?

What does “yield break;” do in C#?

我在msdn中见过这个语法:yield break,但我不知道它有什么作用。有人知道吗?


它指定迭代器已经结束。您可以将yield break看作是不返回值的return语句。

例如,如果将函数定义为迭代器,则函数体可能如下所示:

1
2
3
4
5
6
for (int i = 0; i < 5; i++)
{
    yield return i;
}

Console.Out.WriteLine("You will see me");

请注意,在循环完成所有循环后,最后一行将被执行,您将在控制台应用程序中看到该消息。

或者像这样用yield break

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int i = 0;
while (true)
{
    if (i < 5)
    {
        yield return i;
    }
    else
    {
        // note that i++ will not be executed after this
        yield break;
    }
    i++;
}

Console.Out.WriteLine("Won't see me");

在这种情况下,不会执行最后一条语句,因为我们提前离开了函数。


结束迭代器块(例如说IEnumerable中没有其他元素)。


告诉迭代器它已到达末尾。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface INode
{
    IEnumerable<Node> GetChildren();
}

public class NodeWithTenChildren : INode
{
    private Node[] m_children = new Node[10];

    public IEnumerable<Node> GetChildren()
    {
        for( int n = 0; n < 10; ++n )
        {
            yield return m_children[ n ];
        }
    }
}

public class NodeWithNoChildren : INode
{
    public IEnumerable<Node> GetChildren()
    {
        yield break;
    }
}

yield基本上使IEnumerable方法的行为类似于协作(而不是抢先)调度线程。

yield return就像一个线程调用"schedule"或"sleep"函数来放弃对CPU的控制。就像线程一样,IEnumerable方法在随后立即恢复控制,所有局部变量的值与放弃控制之前的值相同。

yield break就像一个线程到达其功能的末尾并终止。

人们谈论的是"状态机",但状态机其实都是"线程"。线程有一些状态(即局部变量的值),每次调度线程时,它都会采取一些操作以达到新的状态。关于yield的关键点是,与我们所使用的操作系统线程不同,使用它的代码会被及时冻结,直到手动提前或终止迭代。


迭代器块的整个主题在乔恩·斯基特的书C中的这个自由示例章节中有很好的介绍。


这里http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/就是一个很好的例子:

1
2
3
4
5
6
7
8
9
10
11
public static IEnumerable<int> Range( int min, int max )
{
   while ( true )
   {
      if ( min >= max )
      {
         yield break;
      }
      yield return min++;
   }
}

并且解释说,如果在一个方法中命中一个yield break语句,那么该方法的执行将停止而不返回。有时情况下,当你不想给出任何结果时,你可以使用收益率中断。


yield break语句导致枚举停止。实际上,yield break在不返回任何附加项的情况下完成了枚举。

考虑到迭代器方法实际上有两种方法可以停止迭代。在一种情况下,该方法的逻辑可以在返回所有项之后自然地退出该方法。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
IEnumerable<uint> FindPrimes(uint startAt, uint maxCount)
{
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;
    }

    Debug.WriteLine("All the primes were found.");
}

在上面的示例中,一旦找到maxCount素数,迭代器方法将自然停止执行。

yield break语句是迭代器停止枚举的另一种方法。这是一种早期脱离枚举的方法。这里的方法和上面的一样。这一次,该方法对该方法可以执行的时间量有限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
IEnumerable<uint> FindPrimes(uint startAt, uint maxCount, int maxMinutes)
{
    var sw = System.Diagnostics.Stopwatch.StartNew();
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;

        if (sw.Elapsed.TotalMinutes > maxMinutes)
            yield break;
    }

    Debug.WriteLine("All the primes were found.");
}

注意对yield break的调用。实际上,它正在提前退出枚举。

也要注意,yield break的工作方式与普通break的工作方式不同。在上面的示例中,yield break退出方法,而不调用Debug.WriteLine(..)


如果你所说的"产量突破真正起作用的是什么",那就是"它是如何起作用的"—有关详细信息,请参阅Raymond Chen的博客:http://blog s.msdn.com/oldnewthing/archive/2008/08/12/8849519.aspx

C迭代器生成一些非常复杂的代码。


收益率突破只是最后一次说回报的一种方式,不返回任何价值。

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// returns 1,2,3,4,5
IEnumerable<int> CountToFive()
{
    yield return 1;
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
    yield break;
    yield return 6;
    yield return 7;
    yield return 8;
    yield return 9;
 }

yield关键字与return关键字一起使用,为枚举器对象提供一个值。yield return指定返回的值。当到达Yield RETURN语句时,将存储当前位置。下次调用迭代器时,将从此位置重新启动执行。

用一个例子解释其含义:

1
2
3
4
5
6
7
8
9
10
11
12
13
    public IEnumerable<int> SampleNumbers()
    {
        int counter = 0;
        yield return counter;

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

迭代时返回的值为:0、2、5。

需要注意的是,本例中的计数器变量是局部变量。在返回值2的第二次迭代之后,第三次迭代从它离开之前的位置开始,同时保留名为counter的局部变量以前的值2。