我最近一直在玩各种类型的PriorityQueue类,我遇到过一些我不太了解的行为。
这里是我正在运行的单元测试的片段:
PriorityQueue<Int32> priorityQueue = new PriorityQueue<Int32>();
Randomizer r = new Randomizer();
priorityQueue.AddRange(r.GetInts(Int32.MinValue, Int32.MaxValue, r.Next(300, 10000)));
priorityQueue.PopFront(); // Gets called, and works correctly
Int32 numberToPop = priorityQueue.Count / 3;
priorityQueue.PopFront(numberToPop); // Does not get called, an empty IEnumberable<T> (T is an Int32 here) is returned
正如我在评论中所指出的,PopFront()被调用并正常运行,但是当我尝试调用PopFront(numberToPop)时,该方法根本没有被调用,因为它甚至没有进入方法
以下是方法:
public T PopFront()
{
if (items.Count == 0)
{
throw new InvalidOperationException("No elements exist in the queue");
}
T item = items[0];
items.RemoveAt(0);
return item;
}
public IEnumerable<T> PopFront(Int32 numberToPop)
{
Debug.WriteLine("PriorityQueue<T>.PopFront({0})", numberToPop);
if (numberToPop > items.Count)
{
throw new ArgumentException(@"The numberToPop exceeds the number
of elements in the queue", "numberToPop");
}
while (numberToPop-- > 0)
{
yield return PopFront();
}
}
现在,我之前已经实现了重载的PopFront函数,如下所示:
public IEnumerable<T> PopFront(Int32 numberToPop)
{
Console.WriteLine("PriorityQueue<T>.PopFront({0})", numberToPop);
if (numberToPop > items.Count)
{
throw new ArgumentException(@"The numberToPop exceeds the number
of elements in the queue", "numberToPop");
}
var poppedItems = items.Take(numberToPop);
Clear(0, numberToPop);
return poppedItems;
}
之前的实施(上文)按预期工作。尽管如此,我显然知道我对yield
语句的使用是不正确的(很可能因为我正在删除然后返回PopFront()函数中的元素),但我真正感兴趣的是为什么从不调用PopFront(Int32 numberToPop),如果没有调用它,为什么它会返回一个空的IEnumerable?
非常感谢任何帮助/解释为什么会发生这种情况。
答案 0 :(得分:5)
使用yield return
时,编译器会为您创建状态机。在您开始枚举(foreach
或ToList
)您的方法返回的IEnumerable<T>
之前,您的代码才会开始执行。
在foreach循环的迭代中,为元素调用MoveNext方法。此调用执行MyIteratorMethod的主体,直到达到下一个yield return语句。 yield return语句返回的表达式不仅决定了循环体消耗的元素变量的值,还决定了元素的Current属性,即IEnumerable。
在foreach循环的每个后续迭代中,迭代器主体的执行从它停止的地方继续,当它到达yield return语句时再次停止。当迭代器方法结束或yield break语句到达时,foreach循环完成。