Eiffel 的附加语句中的局部变量有什么意义?
What's the point of the local variable in Eiffel's attached-statement?
在 Eiffel 中,Void Safety 是一种静态 防止取消引用未初始化 ("null") 对象的方法。它的工作方式是,首先,对象必须声明为 可分离,然后你需要检查一个 if 块是否对象实际上是 attached(即有一些价值)才能使用它。
这是我到现在为止一直使用它的方式:
some_object: detachable TYPE
...
if attached some_object then
some_object.method
end
工作得很好:没有附加检查,编译失败并出现 "Target of the Object_call might be void" 错误。
然而,在实际阅读了有关 Void Safety 的 documentation 之后,我了解到这实际上应该是这样的:
some_object: detachable TYPE
...
if attached some_object as l_some_object then
l_some_object.method
end
在这种形式中,l_some_object
是 if-block 的局部变量 ,它指向与 some_object
相同的对象,但静态保证是非空的。
但是,我看不出存在这个 as 子句的原因。正如我在上面指出的那样,显然原始 some_object
已经静态地保证在 if 块中是非空的,那么引入另一个变量有什么意义呢?
除了范围之外,some_object
和l_some_object
有什么区别?
简答
如果some_object
是一个局部变量,引入对象test locall_some_object
没有意义。
长答案
对象测试的一般形式是
attached {SOME_TYPE} expr as var
其中 {SOME_TYPE}
和 var
是可选的。当不使用类型(上例中的{SOME_TYPE}
)时,对象测试只是检查是否附加了expr
,并在附加时将其值赋给var
。
理论上像下面这样的东西应该是无效安全的:
if attached expr then
expr.do_something
end
但是在一般情况下这是不允许的,因为 expr
可能有副作用,所以第二次计算时,返回不同的值,这个值可能 void
代码 void-unsafe:
if attached foo then -- On first call function foo returns non-void value.
foo.do_something -- On second call function foo returns void: BOOM!
end
另一种可能性是改变表达式值的中间调用,例如,
if attached attr then -- Attribute attr is attached here.
bar -- bar sets attr to Void.
attr.do_something -- BOOM!
end
如果 bar
将属性 attr
设置为 void
(这可以间接完成),代码再次无效。
最后,在多线程环境中,即使没有任何中间功能调用,另一个线程也可能在检查之后和在 "then" 部分使用之前更改 attr
的值:
if attached attr then -- Attribute attr is attached here.
-- Another thread sets attr to Void.
attr.do_something -- BOOM!
end
为了防止出现这些情况,使用了 var
部分。此对象 test local 是只读的,并且不受同一表达式的评估、任何中间功能调用或另一个线程的影响。换句话说,它总是附加的。
在某些情况下,对象测试表达式不受这些因素的影响:
参数是只读的,所以使用缩写形式总是足够的
attached arg
并且引入一个对象 test local 是没有意义的,因为它总是等于参数。
局部变量和 Result
只有在分配了可分离表达式时才可能变为 Void
。如果没有这样的赋值,一样
attached local_var
就好了。然而,一旦本地被分配了一个可分离的表达式,它就不再被认为是附加的:
if attached local_var then
... -- OK to use local_var as attached.
local_var := detachable_expression
... -- No guarantees about local_var attachment status.
end
如果不需要这种场景,可以使用对象测试的长格式
attached local_var as attached_local_var
并保证 attached_local_var
始终附加。
在 Eiffel 中,Void Safety 是一种静态 防止取消引用未初始化 ("null") 对象的方法。它的工作方式是,首先,对象必须声明为 可分离,然后你需要检查一个 if 块是否对象实际上是 attached(即有一些价值)才能使用它。
这是我到现在为止一直使用它的方式:
some_object: detachable TYPE
...
if attached some_object then
some_object.method
end
工作得很好:没有附加检查,编译失败并出现 "Target of the Object_call might be void" 错误。 然而,在实际阅读了有关 Void Safety 的 documentation 之后,我了解到这实际上应该是这样的:
some_object: detachable TYPE
...
if attached some_object as l_some_object then
l_some_object.method
end
在这种形式中,l_some_object
是 if-block 的局部变量 ,它指向与 some_object
相同的对象,但静态保证是非空的。
但是,我看不出存在这个 as 子句的原因。正如我在上面指出的那样,显然原始 some_object
已经静态地保证在 if 块中是非空的,那么引入另一个变量有什么意义呢?
除了范围之外,some_object
和l_some_object
有什么区别?
简答
如果some_object
是一个局部变量,引入对象test locall_some_object
没有意义。
长答案
对象测试的一般形式是
attached {SOME_TYPE} expr as var
其中 {SOME_TYPE}
和 var
是可选的。当不使用类型(上例中的{SOME_TYPE}
)时,对象测试只是检查是否附加了expr
,并在附加时将其值赋给var
。
理论上像下面这样的东西应该是无效安全的:
if attached expr then
expr.do_something
end
但是在一般情况下这是不允许的,因为 expr
可能有副作用,所以第二次计算时,返回不同的值,这个值可能 void
代码 void-unsafe:
if attached foo then -- On first call function foo returns non-void value.
foo.do_something -- On second call function foo returns void: BOOM!
end
另一种可能性是改变表达式值的中间调用,例如,
if attached attr then -- Attribute attr is attached here.
bar -- bar sets attr to Void.
attr.do_something -- BOOM!
end
如果 bar
将属性 attr
设置为 void
(这可以间接完成),代码再次无效。
最后,在多线程环境中,即使没有任何中间功能调用,另一个线程也可能在检查之后和在 "then" 部分使用之前更改 attr
的值:
if attached attr then -- Attribute attr is attached here.
-- Another thread sets attr to Void.
attr.do_something -- BOOM!
end
为了防止出现这些情况,使用了 var
部分。此对象 test local 是只读的,并且不受同一表达式的评估、任何中间功能调用或另一个线程的影响。换句话说,它总是附加的。
在某些情况下,对象测试表达式不受这些因素的影响:
参数是只读的,所以使用缩写形式总是足够的
attached arg
并且引入一个对象 test local 是没有意义的,因为它总是等于参数。
局部变量和
Result
只有在分配了可分离表达式时才可能变为Void
。如果没有这样的赋值,一样attached local_var
就好了。然而,一旦本地被分配了一个可分离的表达式,它就不再被认为是附加的:
if attached local_var then ... -- OK to use local_var as attached. local_var := detachable_expression ... -- No guarantees about local_var attachment status. end
如果不需要这种场景,可以使用对象测试的长格式
attached local_var as attached_local_var
并保证
attached_local_var
始终附加。