我的印象是Ruby中的类定义可以重新打开:
class C
def x
puts 'x'
end
end
class C
def y
puts 'y'
end
end
这将按预期工作,y
将添加到原始类定义中。
我很困惑为什么以下代码无法按预期工作:
class D
x = 12
end
class D
puts x
end
这将导致NameError
异常。为什么重新打开类时会启动新的本地范围?这似乎有点违反直觉。扩展类时,有没有办法继续以前的本地作用域?
局部变量不与对象关联,它们与作用域相关联。
局部变量按词法作用域。您尝试执行的操作不会比以下操作更有效:
def foo
x = :hack if false # Ensure that x is a local variable
p x if $set # Won't be called the first time through
$set = x = 42 # Set the local variable and a global flag
p :done
end
foo #=> :done
foo #=> nil (x does not have the value from before)
#=> done
在上面,它是相同的方法,在同一个对象上,两次都被执行。self
不变。但是,局部变量在调用之间被清除。
重新打开类就像再次调用方法:您处于相同的self
范围内,但您正在启动一个新的本地上下文。当您使用 end
关闭 class D
块时,您的局部变量将被丢弃(除非它们被关闭)。
在 ruby 中,局部变量只能在定义它们的范围内访问。但是,class
关键字会导致一个全新的范围。
class D
# One scope
x = 12
end
class D
# Another scope
puts x
end
因此,您无法访问在第一个class
部分中定义的局部变量,因为当您离开第一个作用域时,其中的局部变量将被破坏,内存被垃圾回收释放。
例如,对于def
也是如此。
简单的解决方案是将 x 作为类实例变量。 当然,这并不能回答您的范围问题。 我相信 x 不在重新打开的类的作用域(请参阅检测 Ruby 变量的作用域)的原因是因为它的作用域从一开始就不是类 D 的作用域。 x 的作用域在类 D 关闭时结束,因为 x 既不是实例变量也不是类变量。
我希望这有所帮助。
这种行为有意义的部分原因在于元编程功能;您可以使用一些临时变量来存储可用于命名新常量或引用方法名称的数据:
class A
names, values = get_some_names_and_values()
names.each_with_index do |name, idx|
const_set name, value[idx]
end
end
或者,也许,您需要获得特征类...
class B
eigenclass = class << self; self; end
eigenclass.class_eval do
...
end
end
每次重新打开类时都有一个新作用域是有意义的,因为在 Ruby 中,您经常在类定义中编写代码,该定义是要执行的实际代码,打开类只是获取正确值的一种方式self
。您不希望类的内容被这些变量污染,否则您可以使用类实例变量:
class C
@answer = 42
end
class C
p @answer
end