时间:2019-03-17 标签:c#yieldandtry-finally

发布于 2024-11-09 20:43:29 字数 431 浏览 2 评论 0原文

如果我有一个如下的协程,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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

过期情话 2024-11-16 20:43:30

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

As long as the iterator/enumerator is disposed properly (IDisposable.Dispose() is called) then yes:

Control is always passed to the finally block regardless of how the try block exits.

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:

A yield return statement cannot be located anywhere inside a try-catch block. It can be located in a try block if the try block is followed by a finally block.

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

诗笺 2024-11-16 20:43:30

到目前为止,所有答案都忽略了一个关键细节:包装 yield returnfinally 块中的代码将在 IDisposable.Dispose 时执行调用执行yield return的迭代器/枚举器。如果外部代码在迭代器上调用 GetEnumerator(),然后调用 MoveNext(),直到迭代器在 内执行 yield return finally 块,然后外部代码放弃枚举器而不调用 Disposefinally 块中的代码将不会运行。根据迭代器正在执行的操作,它可能会被垃圾收集器消灭(尽管没有机会清理任何外部资源),或者最终可能会永久或半永久地导致内存泄漏(如果出现以下情况,则可能会发生这种情况:例如,它将 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 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.

中性美 2024-11-16 20:43:30

如果您只是懒得添加 Main() 等,请从此处获取代码,运行它并看看会发生什么:

YieldReturnAndFinally

对 @Henk Holterman 评论的回应

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

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

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:

* IEnumerable
* IEnumerable<T>
* IEnumerator
* IEnumerator<T>
想你只要分分秒秒 2024-11-16 20:43:30

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

由于您使用的是yield,因此直到您访问该方法返回的IEnumerator 后,finally 块才会被执行。例如:

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

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

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:

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

    //Console.WriteLines will happen when the following
    //statement is executed
    var y = x.MoveNext();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文