时间:2019-03-17 标签:c#yieldandtry-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!");
}
}
If I have a coroutine as follows, will the code in the finally block get called?
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!");
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
只要正确处置迭代器/枚举器(调用
IDisposable.Dispose()
),那么是的:http://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx
但是,请注意
Dispose()
确实被调用,否则您最多可能会得到意想不到的结果。有关此现象的更多信息以及需要注意的一些问题,请查看此博客文章:产量和使用 -您的 Dispose 可能不会被调用!
(感谢 Scott B 提供链接,由于每个人似乎都错过了答案,因此将其放入答案中)
另外:
http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
As long as the iterator/enumerator is disposed properly (
IDisposable.Dispose()
is called) then yes:http://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx
However, be careful that
Dispose()
is indeed called, or else you may end up with unintended results at best. For more information on this phenomenon and some gotchas to watch out for, check out this blog post:Yield and usings - your Dispose may not be called!
(Thanks to Scott B for providing the link, placing in the answer since everybody seems to be missing it)
Additionally:
http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
到目前为止,所有答案都忽略了一个关键细节:包装
yield return
的finally
块中的代码将在IDisposable.Dispose
时执行调用执行yield return
的迭代器/枚举器。如果外部代码在迭代器上调用GetEnumerator()
,然后调用MoveNext()
,直到迭代器在内执行
块,然后外部代码放弃枚举器而不调用yield return
finallyDispose
,finally
块中的代码将不会运行。根据迭代器正在执行的操作,它可能会被垃圾收集器消灭(尽管没有机会清理任何外部资源),或者最终可能会永久或半永久地导致内存泄漏(如果出现以下情况,则可能会发生这种情况:例如,它将 lambda 表达式附加到长期对象的事件处理程序)。请注意,虽然 vb 和 c# 都非常擅长确保
foreach
循环对枚举器调用Dispose
,但可以通过调用GetEnumerator()
来使用迭代器code> 显式地执行此操作,并且某些代码可能会在不调用Dispose()
的情况下执行此操作。迭代器对此无能为力,但任何编写迭代器的人都需要意识到这种可能性。All of the answers so far omit a crucial detail: code in a
finally
block which wraps ayield return
will execute if and whenIDisposable.Dispose
is called upon the iterator/enumerator which executed theyield return
. If outside code callsGetEnumerator()
on an iterator and then, callsMoveNext()
until the iterator performs theyield return
within afinally
block, and the outside code then abandons the enumerator without callingDispose
, the code in thefinally
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 callDispose
on enumerators, it's possible to use iterators by callingGetEnumerator()
explicitly, and it's possible some code might do so without callingDispose()
. There isn't much an iterator can do about that, but anyone writing iterators needs to be aware of the possibility.如果您只是懒得添加
Main()
等,请从此处获取代码,运行它并看看会发生什么:YieldReturnAndFinally
对 @Henk Holterman 评论的回应
以下四个中的任何一个是 有效的:
If you were just lazy to add
Main()
etc, get the code from here, run it and see what happens:YieldReturnAndFinally
Response to @Henk Holterman's comment
Any of the below four is valid:
根据 文档,是的,finally中的代码总是会被调用。
由于您使用的是yield,因此直到您访问该方法返回的IEnumerator 后,finally 块才会被执行。例如:
According to the documentation, yes, code in the finally will always be called.
Since you are using yield, the finally block will not be executed until you access the IEnumerator returned by the method. For example: