调用包含yield的方法后,不执行代码行 [英] lines of code are not executed after calling method containing yield

查看:527
本文介绍了调用包含yield的方法后,不执行代码行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下方法:

IEnumerable<DateTime> GetTimes(int count)
{
 for (int i = 0; i < count; i++)
      yield return DateTime.Now;
 yield break;
}

现在,我要称呼它:

 var times = GetTimes(2);
 Console.WriteLine("First element:" + times.Take(1).Single().ToString());
 Console.WriteLine("Second element:" + times.Skip(1).Take(1).Single().ToString());
 Console.WriteLine("Third element:" + times.Skip(2).Take(1).Single().ToString());
 Console.WriteLine("Finished...");

但是最后一行代码永远不会运行.为什么?

But the last line of code never runs. Why?

推荐答案

由于执行迭代器方法GetTimes(int count)时不执行

The iterator method GetTimes(int count) is not executed when you do

var times = GetTimes(2);

相反,只要您从值中提取一个值(例如,当您执行times.Take(1).Single().ToString()时),它就会执行.

Rather, it is executed whenever you are pulling a value from it (for example, when you do times.Take(1).Single().ToString()).

有两件事在这里产生这种看似奇怪的行为:

There are two things that produce this seemingly weird behavior here:

  1. 只要按下yield return行,迭代器就会停止.当您尝试从迭代器中获取另一个元素时,迭代器执行将从其离开的点开始恢复.如果您不这样做,它将永远不会恢复执行.

  1. Iterators stop whenever a yield return line is hit. The iterator execution resumes from the point where it left when you try to get another element from it. If you don't it will never resume execution.

您实际上两次执行了迭代器.您执行的操作通常称为"IEnumerable的多个枚举"

You are actually executing the iterator twice. You do something that's usually referred to as "multiple enumeration of IEnumerable"

为了说明幕后实际发生的情况,让我们对您的GetTimes方法进行一些小的更改:我们不会每次都返回相同的日期,但是每次调用时,我们都会返回前一个日期+ 1天.我们还添加一些Console.WriteLine来跟踪执行.因此,新方法的主体可能如下所示:

To illustrate what actually happens behind the scenes, let's to a small change to your GetTimes method: let's not return the same date every time, but each time it's called we'll return the previous date + 1 day. Let's also add some Console.WriteLine to trace execution. So, the new method body could look like this:

IEnumerable<DateTime> GetTimes(int count)
{
    for (int i = 0; i < count; i++)
    {
        Console.WriteLine("returning the value with index " + i);
        yield return DateTime.Now.AddDays(i);
    }

    Console.WriteLine("About to hit the `yield break`! Awesome!");
    yield break;
}

现在运行代码将产生以下输出:

Running your code now produces the following output:


returning the value with index 0
First element:11/16/2012 11:34:46 PM
returning the value with index 0
returning the value with index 1
Second element:11/17/2012 11:34:46 PM
Finished...

这说明了以上两点:

    返回值后,
  1. GetTimes执行立即停止,并在请求另一个值时从相同状态恢复执行.

  1. GetTimes execution is stopped as soon as a value is returned and it is resumed from the same state as soon as another one is requested.

迭代器执行两次(第二次使用times时,Skip请求一个值,而Take请求下一个值并使用它).

The Iterator is executed twice (the second time you use times the Skip requests a value and the Take requests the next value and uses it).

好,但是为什么yield break;不被执行?这是因为您的迭代器可以产生2个值,并且仅被调用2次,因此在命中第二个yield return之后将其冻结. 如果您要从迭代器中请求第三个元素,那么您的break行将被命中.

OK, but why doesn't the yield break; get executed? That's because your iterator can produce 2 values and it's called only 2 times, making it freeze after the second yield return is hit. If you were to request the third element from the iterator, then your break line would be hit.

现在,为了说明最后一行被命中的情况,让我们以通常的方式使用枚举器(使用foreach循环).将Console.WriteLine行替换为:

Now, to illustrate a scenario in which the last line gets hit, let's use the enumerator in a usual way (using a foreach loop). Replace your Console.WriteLine lines with:

foreach (var dateTime in times)
    Console.WriteLine(dateTime);

此代码将产生以下输出:

This code will produce the following output:


returning the value with index 0
11/17/2012 12:05:20 AM
returning the value with index 1
11/18/2012 12:05:20 AM
About to hit the `yield break`! Awesome!

如您所见,foreach一直消耗迭代器直到结尾,并且yield break行被命中.您还可以通过在其上设置断点来确认这一点.

As you can see, the foreach consumes the iterator all the way to the end and the yield break line gets hit. You can also confirm this by setting a breakpoint on it.

这篇关于调用包含yield的方法后,不执行代码行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆