5

如果我有如下协程,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!");
  }
}
4

4 回答 4

20

只要迭代器/枚举器被正确处理(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

于 2011-05-25T18:43:16.573 回答
16

All of the answers so far omit a crucial detail: code in a finally block which wraps a yield return will execute if and when IDisposable.Dispose is called upon the iterator/enumerator which executed the yield return. If outside code calls GetEnumerator() on an iterator and then, calls MoveNext() until the iterator performs the yield return within a finally block, and the outside code then abandons the enumerator without calling Dispose, the code in the finally block will not run. Depending upon what the iterator was doing, it may get annihilated by the garbage collector (though without having a chance at cleaning up any outside resources) or it may end up permanently or semi-permanently rooted as a memory leak (that could happen if, for example, it attached a lambda expression to a long-lived object's event handler).

Note that while both vb and c# are very good about ensuring that foreach loops will call Dispose on enumerators, it's possible to use iterators by calling GetEnumerator() explicitly, and it's possible some code might do so without calling Dispose(). There isn't much an iterator can do about that, but anyone writing iterators needs to be aware of the possibility.

于 2011-12-06T21:57:11.443 回答
1

根据文档,是的,finally 中的代码总是会被调用。

由于您使用的是 yield,因此在您访问该方法返回的 IEnumerator 之前,不会执行 finally 块。例如:

void Main()
{
    var x = MyCoroutine(12);

    //Console.WriteLines will happen when the following
    //statement is executed
    var y = x.MoveNext();
}
于 2011-05-25T18:41:33.647 回答
1

如果你只是懒得添加Main()等,从这里获取代码,运行它,看看会发生什么:

YieldReturnAndFinally

回复@Henk Holterman 的评论

以下四个中的任何一个都有效:

* IEnumerable
* IEnumerable<T>
* IEnumerator
* IEnumerator<T>
于 2011-05-25T18:47:32.233 回答