今天我在Pharo中了解到执行:
[v := 1] ensure: [self halt. v := 2]
即使我们在v = 2,
窗口(!)放弃了该过程,也会设置halt
。
我觉得这个值得商榷。对我来说,#ensure:
的语义意味着序列
self halt. v := 2
无论接收器块的情况如何,都必须执行,不管参数块的逻辑。由于#halt
的逻辑包括终止过程的事件,我发现它对第二句的顽固评估具有侵扰性。
接下来我尝试了以下内容:
[v := 1] ensure: [1 / 0. v := 2]
当弹出ZeroDivide
异常时,我关闭了调试器,但v
的值仍为2
(与#halt
相同。)
最后,我评估了:
[v := 1] ensure: [n := 1 / 0. v := v + n]
并关闭ZeroDivide
异常上的调试器。这次v
的值为1
但我无法评估v + n
无法评估的事实。换句话说,错误继续无声。
所以我的问题是。这种行为背后的理性是什么?该过程不应该终止于它将终止于" normal"情况,即没有涉及#ensure:
?
答案 0 :(得分:4)
有趣的一个。您的答案似乎取决于BlockClosure>>valueNoContextSwitch
调用的#ensure:
方法。如果您在那里阅读注释,则表示它创建了BlockClosure>>value
(在基元中)的精确副本,并返回该副本的返回值,而不是包含{{1}的原始块的返回值你终止了。因此副本会被执行(显然忽略了复制的halt
),即使原始文件没有完成。
我的猜测是,这是为了确保(没有双关语)halt
块总是运行,但是有无意识的副作用,忽略了原始块的终止。我同意你的看法,这不仅违反直觉,而且可能也不符合预期。
答案 1 :(得分:1)
我想这是任何(ANSI)标准都没有完全定义的行为,但如果我错了,请纠正我。
其他Smalltalks似乎表现不同。我在Smalltalk / X中尝试过,其中调试器提供3个选项:“继续”(即继续),“中止”(即退出)和“终止”(即终止进程)。我猜“终止”对应于关闭调试器时Squeak的作用。
使用“Abort”和“Terminate”,保证块的其余部分不会执行,“Continue”就是。我想这没关系,你会期待什么。
On Abort和Terminate(它们都是对抗异常处理程序的解除),它不应该尝试重新评估或继续潜在的错误/错误/失败的确保块。
如果要继续或不进行处理程序(调试器基本上是)的选择。如果没有,那么它应该离开ensure块并继续执行可能在调用链中的任何其他保证块。
这与异常处理块的行为一致,如果在其中引发相同的异常,也不会重新评估或继续执行。在ST / X中,异常类中有明确的代码来处理这种情况,所以它绝对是出于目的而不是副作用。
我的猜测是,这在Squeak中是错误的,应该告诉吱吱作响的开发人员。