当前位置:  开发笔记 > 编程语言 > 正文

为什么不能在带有catch的try块内出现yield return?

如何解决《为什么不能在带有catch的try块内出现yieldreturn?》经验,为你挑选了2个好方法。

以下是可以的:

try
{
    Console.WriteLine("Before");

    yield return 1;

    Console.WriteLine("After");
}
finally
{
    Console.WriteLine("Done");
}

finally块在整个执行完毕后运行(IEnumerator支持IDisposable提供一种方法来确保即使枚举在完成之前被放弃).

但这不是好事:

try
{
    Console.WriteLine("Before");

    yield return 1;  // error CS1626: Cannot yield a value in the body of a try block with a catch clause

    Console.WriteLine("After");
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
}

假设(为了参数)WriteLinetry块中的一个或另一个调用抛出异常.在catch块中继续执行有什么问题?

当然,收益率回报部分(目前)无法抛出任何东西,但是为什么要阻止我们封闭try/ catch处理在之前或之后抛出的异常yield return

更新:Eric Lippert在这里有一个有趣的评论 - 似乎他们已经有足够的问题正确实现try/finally行为!

编辑:此错误的MSDN页面是:http://msdn.microsoft.com/en-us/library/cs1x15az.aspx.但这并没有解释为什么.



1> Jon Skeet..:

我怀疑这是一个实用性而非可行性的问题.我怀疑很少有这种限制实际上是一个无法解决的问题 - 但编译器中增加的复杂性将非常重要.

我已经遇到过这样的一些事情:

属性不能通用

X无法从XY派生(X中的嵌套类)

Iterator在生成的类中使用公共字段

在每种情况下,都有可能获得更多的自由,代价是编译器的额外复杂性.团队做出了务实的选择,我为此鼓掌 - 我宁愿用一种稍微限制一点的语言,准确率达到99.9%的编译器(是的,有错误;我在前几天碰到了一个)而不是更多灵活的语言,无法正确编译.

编辑:这是一个伪证明它为什么可行的原因.

考虑一下:

您可以确保yield return部分本身不会抛出异常(预先计算值,然后您只是设置一个字段并返回"true")

你可以在迭代器块中使用try/catch,它不使用yield return.

迭代器块中的所有局部变量都是生成类型中的实例变量,因此您可以自由地将代码移动到新方法

现在变换:

try
{
    Console.WriteLine("a");
    yield return 10;
    Console.WriteLine("b");
}
catch (Something e)
{
    Console.WriteLine("Catch block");
}
Console.WriteLine("Post");

进入(有点伪代码):

case just_before_try_state:
    try
    {
        Console.WriteLine("a");
    }
    catch (Something e)
    {
        CatchBlock();
        goto case post;
    }
    __current = 10;
    return true;

case just_after_yield_return:
    try
    {
        Console.WriteLine("b");
    }
    catch (Something e)
    {
        CatchBlock();
    }
    goto case post;

case post;
    Console.WriteLine("Post");


void CatchBlock()
{
    Console.WriteLine("Catch block");
}

唯一的重复是设置try/catch块 - 但这是编译器当然可以做的事情.

我可能在这里错过了一些东西 - 如果有的话,请告诉我!


一个很好的概念验证,但是一旦你开始使用`using`和`foreach`之类的东西创建范围,这个策略就会变得很痛苦(可能更多的是对于C#程序员而不是C#编译器编写器).例如:`try {foreach(string s in c){yield return s;}} catch(Exception){}`
"我怀疑很少有这种限制实际上是一个无法解决的问题"这有点像说你不需要例外,因为你可以使用C中常用的错误代码返回策略多年前.我承认技术上的困难可能很重要,但在我看来,这仍然严重限制了"yield"的用处,因为你必须写一些意大利面条代码来解决它.
在某些情况下,这个"功能"实际上需要一些相当丑陋的代码才能解决,请参阅http://stackoverflow.com/questions/5067188/yield-return-with-try-catch-how-can-i-solve-it

2> Mark Cidade..:

yield迭代器定义中的所有语句都转换为状态机中的状态,该状态机有效地使用switch语句来推进状态.如果它确实yieldtry/catch中的语句生成代码,则必须为每个语句复制块中的所有内容,同时排除该try块的每个 yield其他yield语句.这并不总是可行的,特别是如果一个yield语句依赖于前一个语句.


我不认为我买了.我认为这完全可行 - 但非常复杂.
C#中的try/catch块并不意味着可重入.如果将它们拆分,则可以在异常后调用MoveNext()并继续尝试具有可能无效状态的块.
推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有