Question

I have been playing around with various implementations of a PriorityQueue class lately, and I have come across some behavior I do not fully understand.

Here, is a snippet from the unit test I am running:

        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

As I noted in the comments, the PopFront() gets called and operates correctly, but when I try to call the PopFront(numberToPop), the method does not get called at all, as in, it does not even enter the method.

Here are the methods:

    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();
        }
    }

Now, previously, I had implemented the overloaded PopFront function like this:

    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;
    }

The previous implementation (above) worked as expected. With all that being said, I am obviously aware that my use of the yield statement is incorrect (most likely because I am removing then returning elements in the PopFront() function), but what I am really interested in knowing is why the PopFront(Int32 numberToPop) is never even called and, if it is not called, why then is it returning an empty IEnumerable?

Any help/explanation to why this is occurring is greatly appreciated.

Was it helpful?

Solution

When you use yield return, the compiler creates a state machine for you. Your code won't start executing until you start to enumerate (foreach or ToList) the IEnumerable<T> returned by your method.

From the yield documentation

On an iteration of the foreach loop, the MoveNext method is called for elements. This call executes the body of MyIteratorMethod until the next yield return statement is reached. The expression returned by the yield return statement determines not only the value of the element variable for consumption by the loop body but also the Current property of elements, which is an IEnumerable.

On each subsequent iteration of the foreach loop, the execution of the iterator body continues from where it left off, again stopping when it reaches a yield return statement. The foreach loop completes when the end of the iterator method or a yield break statement is reached.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top