繁体   English   中英

c# 产量和试一试

[英]c# yield and 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!");
  }
}

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

到目前为止,所有答案都忽略了一个关键细节:如果在执行yield return的迭代器/枚举器上调用IDisposable.Dispose时,包装yield returnfinally块中的代码将执行。 如果外部代码在迭代器上调用GetEnumerator() ,然后调用MoveNext() ,直到迭代器在finally块中执行yield return ,然后外部代码放弃枚举器而不调用Disposefinally块中的代码将不会运行. 根据迭代器所做的事情,它可能会被垃圾收集器消灭(尽管没有机会清理任何外部资源),或者它可能最终永久或半永久地扎根为 memory 泄漏(如果发生这种情况,例如,它将 lambda 表达式附加到长期对象的事件处理程序)。

请注意,虽然 vb 和 c# 在确保foreach循环将在枚举器上调用Dispose方面都非常出色,但可以通过显式调用GetEnumerator()来使用迭代器,并且某些代码可能会在不调用Dispose()的情况下这样做。 迭代器对此无能为力,但任何编写迭代器的人都需要意识到这种可能性。

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

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

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

    //Console.WriteLines will happen when the following
    //statement is executed
    var y = x.MoveNext();
}

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

YieldReturnAndFinally

回复@Henk Holterman 的评论

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

* IEnumerable
* IEnumerable<T>
* IEnumerator
* IEnumerator<T>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM