【问题标题】:Strange execution order when using nested method, yield return and using in combination [duplicate]使用嵌套方法,yield return和组合使用时奇怪的执行顺序[重复]
【发布时间】:2014-09-26 23:48:31
【问题描述】:

我无法理解为什么 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 statement inside a using() { } block Disposes before executing重复

【问题讨论】:

  • 您检查过为此代码生成的 IL 吗?
  • @Alex Voskresenskiy 不,我没有。但是我以前从未做过 IL 考试——所以我必须先弄清楚如何去做。
  • @Martin IL 的收益/回报太可怕了,我不会打扰。您可以尝试使用 ILSpy 进行反编译以查看 C# 中的状态机。

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


【解决方案1】:

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

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

基本上,区别在于Fetch2 中有一层延迟执行(使用yield),而Fetch1 没有,这意味着使用范围在您返回时立即结束。

【讨论】:

  • @I3arnonv 我认为你搞混了:Program.Fetch2 没有添加另一层 - Program.Fetch2 只是调用 Context.Fetch
  • @Martin true,我混合了Context.FetchProgram.Fetch。我更新了答案。
【解决方案2】:

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

所以当你用你的 foreach 实际枚举它时,枚举将发生,Fetch 的代码将实际执行。在一个真实的程序中,你会得到错误,因为你的上下文已经被释放了。

【讨论】:

    猜你喜欢
    • 2017-01-02
    • 2011-02-08
    • 2018-09-13
    • 1970-01-01
    • 1970-01-01
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多