使用嵌套方法、yield return和using in combination时执行顺序奇怪

本文关键字:combination in 执行 顺序 using 嵌套 方法 return yield | 更新日期: 2023-09-27 18:16:21

我无法理解为什么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.DisposeProgram.Fetch1中首先被调用,因为using声明的作用域已经离开了。但Program.Fetch1也是如此。为什么这些方法的行为不同呢?

更新:我的问题是在执行

使用嵌套方法、yield return和using in combination时执行顺序奇怪

之前使用(){}块处置内的yield return语句的副本

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

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

基本上,不同之处在于,在Fetch2中,你有一个延迟执行层(使用yield),而在Fetch1中,你没有,这意味着using作用域在你返回时立即结束。

您的Fetch方法返回一个IEnumerable。当它返回时,using块将处理上下文。然而,IEnumerable只是执行的承诺。它执行。当你列举它的时候。这就是所谓的延迟执行。

因此,当您实际使用foreach枚举它时,枚举将发生并且Fetch的代码将实际执行。在一个真实的程序中,你会得到错误,因为你的上下文已经被处理了。