代码之家  ›  专栏  ›  技术社区  ›  M.kazem Akhgary

dispose是否通过收益率下降到底部?

  •  -1
  • M.kazem Akhgary  · 技术社区  · 7 年前

    IEnumerable<T> Silly<T>(this IEnumerable<T> source)
    {
        foreach(var x in source) yield return x;
    }
    

    我知道这将被编译成一个状态机。但它也类似于

    IEnumerable<T> Silly<T>(this IEnumerable<T> source)
    {
        using(var sillier = source.GetEnumerator())
        {
            while(sillier.MoveNext()) yield return sillier.Current;
        }
    }
    

    现在考虑这个用法

    list.Silly().Take(2).ToArray();
    

    Silly enumerable可能未完全使用,但 Take(2)

    问题: 当调用dispose时 Take sillier 统计员?

    我的猜测是,编译器可以处理这个简单的用例,因为 foreach 但是不那么简单的用例呢?

    IEnumerable<T> Silly<T>(this IEnumerable<T> source)
    {
        using(var sillier = source.GetEnumerator())
        {
            // move next can be called on different stages.
        }
    }
    


    如果未调用dispose,如何使一次性可枚举?


    if(disposed) yield break; 以后 yield return . 现在只需设置愚蠢枚举数的dispose方法 disposed = true

    1 回复  |  直到 7 年前
        1
  •  0
  •   Damien_The_Unbeliever    7 年前

    C#编译器负责 大量 当它将迭代器转换为实际代码时,您可以使用它。例如,下面是 MoveNext 其中包含第二个示例的实现 :

    private bool MoveNext()
    {
        try
        {
            switch (this.<>1__state)
            {
                case 0:
                    this.<>1__state = -1;
                    this.<sillier>5__1 = this.source.GetEnumerator();
                    this.<>1__state = -3;
                    while (this.<sillier>5__1.MoveNext())
                    {
                        this.<>2__current = this.<sillier>5__1.Current;
                        this.<>1__state = 1;
                        return true;
                    Label_005A:
                        this.<>1__state = -3;
                    }
                    this.<>m__Finally1();
                    this.<sillier>5__1 = null;
                    return false;
    
                case 1:
                    goto Label_005A;
            }
            return false;
        }
        fault
        {
            this.System.IDisposable.Dispose();
        }
    }
    

    所以,你会注意到 finally 来自你方的条款 using 根本就不存在,它是一个状态机 2. 这依赖于处于某些良好状态(>=0)以进一步向前推进。(这也是违法的,但是嘿嘿)。

    现在让我们看看它的 Dispose :

    [DebuggerHidden]
    void IDisposable.Dispose()
    {
        switch (this.<>1__state)
        {
            case -3:
            case 1:
                try
                {
                }
                finally
                {
                    this.<>m__Finally1();
                }
                break;
        }
    }
    

    <>m__Finally1 在此处调用(以及由于退出 while 循环 .

    <>m___最终1 :

    private void <>m__Finally1()
    {
        this.<>1__state = -1;
        if (this.<sillier>5__1 != null)
        {
            this.<sillier>5__1.Dispose();
        }
    }
    

    所以,我们可以看到 sillier 被处置 我们进入了一个消极的状态,这意味着 移到下一行 没必要

    所以

    一个想法:可以有一个if(处置)产量中断;每次收益回报后。现在,愚蠢枚举器的dispose方法只需设置disposed=true并移动枚举器一次即可处理所有必需的内容。

    完全没有必要。请相信编译器会转换代码,以便完成所有操作 必然的 它应该做的事情-当迭代器逻辑耗尽或显式处理时,它只运行一次它的finally子句。


    1. 由.NET Reflector生成的所有代码示例。但是现在它太擅长反编译这些构造了,所以如果你去看看 Silly

    [IteratorStateMachine(typeof(<Silly>d__1)), Extension]
    private static IEnumerable<T> Silly<T>(this IEnumerable<T> source)
    {
        IEnumerator<T> <sillier>5__1;
        using (<sillier>5__1 = source.GetEnumerator())
        {
            while (<sillier>5__1.MoveNext())
            {
                yield return <sillier>5__1.Current;
            }
        }
        <sillier>5__1 = null;
    }
    

    它又设法隐藏了关于状态机的大部分细节。您需要追踪 IteratorStateMachine 属性以查看上面显示的所有砂砾位。


    2. 还请注意,编译器正在运行 没有义务 生成允许迭代器工作的状态机。这是当前C#编译器的实现细节。C#规范没有限制 编译器转换迭代器,只是根据效果应该是什么。