c#yield和try-finally

Lir*_*ron 5 c# yield coroutine try-finally

如果我有一个如下的协程,那么finally块中的代码会被调用吗?

public IEnumerator MyCoroutine(int input)
{
  try
  {
    if(input > 10)
    {
      Console.WriteLine("Can't count that high.");
      yield break;
    }
    Console.WriteLine("Counting:");
    for(int i = 0; i < input; i++)
    {
      Console.WriteLine(i.ToString());
      yield return null;
    }
  }
  finally
  {
    Console.WriteLine("Finally!");
  }
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*low 19

只要迭代器/枚举器被正确放置(IDisposable.Dispose()被称为),那么是:

无论try块如何退出,控制总是传递给finally块.

http://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx

但是,要小心Dispose()确实被称为,否则你最多可能会得到意想不到的结果.有关此现象的更多信息以及需要注意的一些问题,请查看此博客文章:

产量和使用 - 您的Dispose可能不会被调用!

(感谢Scott B提供的链接,因为每个人似乎都错过了它所在的答案)

另外:

yield return语句不能位于try-catch块内的任何位置.如果try块后跟finally块,它可以位于try块中.

http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx


sup*_*cat 14

到目前为止,所有的答案都省略了一个至关重要的细节:一个finally包含一个yield return将执行的块的代码if if和when在执行IDisposable.Dispose它的iterator/enumerator上调用yield return.如果外部代码调用GetEnumerator()迭代器然后调用,MoveNext()直到迭代器yield returnfinally块内执行,然后外部代码放弃枚举器而不调用Dispose,则finally块中的代码将不会运行.根据迭代器正在做什么,它可能被垃圾收集器湮灭(虽然没有机会清理任何外部资源),或者它可能永久或半永久地根据内存泄漏(如果,例如,它将一个lambda表达式附加到一个长期存在的对象的事件处理程序中.

请注意,虽然vb和c#都非常适合确保foreach循环将调用Dispose枚举器,但是可以通过GetEnumerator()显式调用来使用迭代器,并且有些代码可能无需调用就可以执行此操作Dispose().迭代器无法做到这一点,但任何编写迭代器的人都需要意识到这种可能性.