C# 在对方法进行枚举时,不能调用Finally块

C# 在对方法进行枚举时,不能调用Finally块,c#,yield,ienumerator,try-finally,C#,Yield,Ienumerator,Try Finally,我发现了一种情况,当最后一个块没有被调用 切中要害: using System; using System.Collections.Generic; using System.Threading; using System.ComponentModel; class MainClass{ static IEnumerable<int> SomeYieldingMethod(){ try{ yield r

我发现了一种情况,当最后一个块没有被调用

切中要害:

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();
            }
        }
    }
所以,看起来如果有人在使用我的屈服方法,并使用枚举数(而不是foreach)手动使用它,那么我的finally块永远无法调用

有没有办法确保我的方法最终确定它的资源?
这是“生成语法糖”中的一个bug,还是它按预期的方式工作?

它按预期的方式工作,最终在出错时执行,或者当你到达块时,如果它们直到最后才枚举,你就永远不会到达该块的末尾,所以最后不应该执行


Yield创建了一个类,将您的方法表示为一个方法+状态放入members+要恢复的位置,因此您每次都在“恢复”该方法,如果您的final被放置在enuemation的末尾,您永远不会到达final&您永远不会枚举到末尾。如果您希望每次都执行finally块,那么您需要进行急切的求值,但您不能说“我希望这是流式的,并在最后被调用,但是让编译器自动在客户端代码中检测它是否永远不会流到最后,然后无论如何都调用finally”。没有“适当”的时间来调用finally,因为您知道他可能会将枚举器传递给另一个项目&让它稍后继续枚举,如果它继续枚举&编译器错误地调用finally会发生什么?

这是因为他们没有正确使用迭代器。该守则应为:

using(var e = SomeYieldingMethod().GetEnumerator())
{
    for (int i = 0; i< iterations; ++i) {
        Console.WriteLine(e.Current);
        e.MoveNext();
    }
}
使用(var e=SomeYieldingMethod().GetEnumerator())
{
对于(int i=0;i
使用
在这里很重要。这就是最终映射到
等的内容(假设迭代器尚未自然完成)


具体来说,
foreach
在迭代器实现
IDisposable
时显式调用迭代器上的
Dispose()
。由于
IEnumerator:IDisposable
,这包括了几乎所有的迭代器(注意:
foreach
不需要
IEnumerator[]
)。

“没有“合适的”时间来最终调用。”-实际上,有很多:时间是定义良好的。当迭代器被释放时(假设它还没有完成),这根本不是重复。这是询问在释放枚举数时是否调用finally块。链接的问题是为什么不能在try/catch块中使用yield。注意,您可以在try/finally块中使用yield,就像这段代码一样。
using(var e = SomeYieldingMethod().GetEnumerator())
{
    for (int i = 0; i< iterations; ++i) {
        Console.WriteLine(e.Current);
        e.MoveNext();
    }
}