枚举overielding方法时,可能不会调用finally块

时间:2013-11-12 13:18:57

标签: c# yield ienumerator try-finally

我发现了一个没有调用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!

所以,看起来如果某人正在使用我的让步方法并使用枚举器(而不是foreach)手动处理它,那么我的finally块永远不会被调用。

有没有办法确保我的方法最终确定它的资源? 这是“产量语法糖”中的一个错误,还是它应该按预期工作?

2 个答案:

答案 0 :(得分:3)

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

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

using 在这里很重要。这是映射到finally等的(假设迭代器还没有自然完成)。

具体来说,如果迭代器实现foreachDispose()显式在迭代器上调用IDisposable。自IEnumerator<T> : IDisposable起,这包括几乎所有迭代器(警告:foreach不需要IEnumerator[<T>])。

答案 1 :(得分:1)

它按预期工作,最终在错误时执行或者当你到达块时,如果它们从不枚举直到结束,你永远不会到达那个块的末尾,所以最终不应该执行。

Yield创建一个类来表示你的方法作为一个方法+状态放入成员+放置的地方,所以你每次都“恢复”这个方法,如果你的最终放在最后,你永远不会到达终点的意见和你永远不会列举到最后。如果你希望每次都执行finally块,你需要做一些急切的评估,但是你不能说“我想让它流式传输,最后在最后被调用,但让编译器自动检测到客户端代码,如果它永远不会流到最后,然后最后调用“。没有“适当”的时间来最终打电话,因为你知道他可能会将调查员传递给另一个项目&amp;让它继续列举,如果它继续列举&amp;编译器错误地调用了最终?