6

我发现未调用 finally 块的情况。

重点:

using System;
using System.Collections.Generic;
using System.Threading;
using System.ComponentModel;

    class MainClass{
        static IEnumerable<int> SomeYieldingMethod(){
            try{
                yield return 1;
                yield return 2;
                yield return 3;
            }finally{
                Console.WriteLine("Finally block!");
            }
        }

        static void Main(){
            Example(7);
            Example(2);
            Example(3);
            Example(4);
        }

        static void Example(int iterations){
            Console.WriteLine("\n Example with {0} iterations.", iterations);
            var e = SomeYieldingMethod().GetEnumerator();
            for (int i = 0; i< iterations; ++i) {
                Console.WriteLine(e.Current);
                e.MoveNext();
            }
        }
    }

结果:

Example with 7 iterations.
0
1
2
3
Finally block!
3
3
3

Example with 2 iterations.
0
1

Example with 3 iterations.
0
1
2

Example with 4 iterations.
0
1
2
3
Finally block!

因此,看起来如果有人正在使用我的 yielding 方法并使用枚举器(而不是 foreach)手动使用它,那么我的 finally 块永远不会被调用。

有什么办法可以确保我的方法最终确定它的资源?这是“产生语法糖”中的一个错误,还是它按预期工作?

4

2 回答 2

3

那是因为他们没有正确使用迭代器。该代码应该是:

using(var e = SomeYieldingMethod().GetEnumerator())
{
    for (int i = 0; i< iterations; ++i) {
        Console.WriteLine(e.Current);
        e.MoveNext();
    }
}

这里using 很重要。这就是映射到finallyetc 的内容(假设迭代器尚未自然完成)。

具体来说,如果迭代器实现了,则foreach显式调用Dispose()迭代IDisposable器。因为IEnumerator<T> : IDisposable,这包括几乎所有的迭代器(警告:foreach不需要IEnumerator[<T>])。

于 2013-11-12T13:25:59.037 回答
1

它按预期工作,最终在错误时执行,或者当您到达该块时,如果它们直到最后都没有枚举,那么您永远不会到达该块的末尾,因此 finally 不应该执行。

Yield 创建一个类来将您的方法表示为方法 + 放入成员的状态 + 恢复的位置,因此您每次都“恢复”该方法,如果您的 finally 放在枚举的末尾,您永远不会到达 finally &你从不枚举,直到最后。如果你希望 finally 块每次都执行,你需要做急切的评估,但你不能说“我希望这个被流式传输,最后在最后被调用,但是让编译器自动检测到客户端代码,如果它永远不会流到最后,然后最终调用”。最后没有“适当的”时间打电话,因为你知道他可能会将枚举器传递给另一个项目并让它在以后继续枚举,如果它继续枚举会发生什么&

于 2013-11-12T13:24:48.180 回答