Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange execution order when using nested method, yield return and using in combination [duplicate]

I'm unable to understand why Program.Fetch1 and Program.Fetch2 do not result in the exact same execution order. The only difference is that Program.Fetch1 is calling Program.Fetch to do the actual fetch operation.

class Program
{
    static IEnumerable<int> Fetch1()
    {
        using (Context c = new Context())
        {
            return Fetch(c);
        }
    }

    static IEnumerable<int> Fetch(Context c)
    {
        foreach (int i in c.Fetch())
        {
            yield return i;
        }
    }

    static IEnumerable<int> Fetch2()
    {
        using (Context c = new Context())
        {
            foreach (int i in c.Fetch())
            {
                yield return i;
            }
        }
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Fetch1:");
        foreach (int i in Fetch1())
        {
            Console.WriteLine(i);
        }
        Console.WriteLine("Fetch2:");
        foreach (int i in Fetch2())
        {
            Console.WriteLine(i);
        }
    }
}


class Context : IDisposable
{

    public void Dispose()
    {
        Console.WriteLine("Context.Dispose");
    }

    public IEnumerable<int> Fetch()
    {
        return new int[] { 1, 2 };
    }
}

Output:

Fetch1:
Context.Dispose
1
2
Fetch2:
1
2
Context.Dispose

My only guess is that Context.Dispose is called first in Program.Fetch1 because the scope of the using declaration was already left. But this is true for Program.Fetch1 as well. So why do those method behave differently?

Update: My question is a duplicate of yield return statement inside a using() { } block Disposes before executing

like image 494
Martin Avatar asked Aug 04 '14 13:08

Martin


People also ask

When using yield return in a method What should the method's return type be?

The yield return statement returns one element at a time. The return type of yield keyword is either IEnumerable or IEnumerator . The yield break statement is used to end the iteration. We can consume the iterator method that contains a yield return statement either by using foreach loop or LINQ query.

What are the two keywords required in an iterator method or property used to return one element at a time?

An object which will return data, one element at a time. Technically speaking, a Python iterator object must implement two special methods, __iter__() and __next__() , collectively called the iterator protocol. An object is called iterable if we can get an iterator from it.

What is the yield keyword used for and what interface is it commonly used in conjunction with?

The yield keyword, first introduced in C# 2.0, T returns an object that implements the IEnumerable interface. The IEnumerable interface exposes an IEnumerator that can used to iterate a non-generic collection using a foreach loop in C#.

Is code after yield executed?

Code is written after the yield statement in the next function call is executed. Code introduced after the return statement will not be executed; instead, it will be ignored. It has the ability to run many times.


1 Answers

That's because those options are actually different:

  • Fetch and Fetch2, using yield, create a state machine to be able to return an unmaterialized IEnumerable.
  • In Fetch1 you are simply calling Fetch and returning the generated state machine and disposing of the context without waiting for that IEnumerable to actually be materialized.

Basically, the difference is that in Fetch2 you have a layer of deferred execution (using yield) while in Fetch1 you don't, which means the using scope ends immediately when you return.

like image 145
i3arnon Avatar answered Oct 12 '22 22:10

i3arnon