使用嵌套方法,收益率返回和组合使用时的奇怪执行顺序

时间:2014-08-04 13:26:10

标签: c# .net idisposable using yield-return

我无法理解为什么Program.Fetch1Program.Fetch2不会导致完全相同的执行顺序。唯一的区别是Program.Fetch1正在调用Program.Fetch来执行实际的提取操作。

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

输出:

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

我唯一的猜测是在Context.Dispose中首先调用Program.Fetch1,因为已使用声明的范围。但对于Program.Fetch1也是如此。那么为什么这些方法表现不同呢?

更新:我的问题与yield return statement inside a using() { } block Disposes before executing

重复

2 个答案:

答案 0 :(得分:6)

那是因为这些选项实际上是不同的:

  • FetchFetch2使用yield创建状态机,以便能够返回未实现的IEnumerable
  • Fetch1中,您只需调用Fetch并返回生成的状态机并处理上下文,而无需等待IEnumerable实际实现。

基本上,不同之处在于,在Fetch2中您有一层延迟执行(使用yield)而在Fetch1中您没有,这意味着使用范围结束你什么时候回来。

答案 1 :(得分:2)

您的Fetch方法会返回IEnumerable。返回此值时,您的using块将处理上下文。但是,IEnumerable只是执行的 promise 。它执行。当你枚举它。这称为deferred execution

因此,当您实际使用foreach枚举它时,将进行枚举,并且Fetch的代码将实际执行。在实际程序中,由于您的上下文已被处理,您将收到错误。