[英]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()
,否则您最多可能会得到意想不到的结果。 有关此现象的更多信息以及需要注意的一些问题,请查看此博客文章:
(感谢 Scott B 提供链接,因为每个人似乎都错过了答案,所以将其放入答案中)
此外:
yield return 语句不能位于 try-catch 块内的任何位置。 如果 try 块后面跟着 finally 块,则它可以位于 try 块中。
到目前为止,所有答案都忽略了一个关键细节:如果在执行yield return
的迭代器/枚举器上调用IDisposable.Dispose
时,包装yield return
的finally
块中的代码将执行。 如果外部代码在迭代器上调用GetEnumerator()
,然后调用MoveNext()
,直到迭代器在finally
块中执行yield return
,然后外部代码放弃枚举器而不调用Dispose
, finally
块中的代码将不会运行. 根据迭代器所做的事情,它可能会被垃圾收集器消灭(尽管没有机会清理任何外部资源),或者它可能最终永久或半永久地扎根为 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()
等,从这里获取代码,运行它,看看会发生什么:
回复@Henk Holterman 的评论
以下四个中的任何一个都有效:
* IEnumerable
* IEnumerable<T>
* IEnumerator
* IEnumerator<T>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.