以下是可以的:
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); }
假设(为了参数)WriteLine
try块中的一个或另一个调用抛出异常.在catch
块中继续执行有什么问题?
当然,收益率回报部分(目前)无法抛出任何东西,但是为什么要阻止我们封闭try
/ catch
处理在之前或之后抛出的异常yield return
?
更新:Eric Lippert在这里有一个有趣的评论 - 似乎他们已经有足够的问题正确实现try/finally行为!
编辑:此错误的MSDN页面是:http://msdn.microsoft.com/en-us/library/cs1x15az.aspx.但这并没有解释为什么.
我怀疑这是一个实用性而非可行性的问题.我怀疑很少有这种限制实际上是一个无法解决的问题 - 但编译器中增加的复杂性将非常重要.
我已经遇到过这样的一些事情:
属性不能通用
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块 - 但这是编译器当然可以做的事情.
我可能在这里错过了一些东西 - 如果有的话,请告诉我!
yield
迭代器定义中的所有语句都转换为状态机中的状态,该状态机有效地使用switch
语句来推进状态.如果它确实为yield
try/catch中的语句生成代码,则必须为每个语句复制块中的所有内容,同时排除该try
块的每个 yield
其他yield
语句.这并不总是可行的,特别是如果一个yield
语句依赖于前一个语句.