我记得在某处读过以下两段代码在Ruby中相同。
# Number 1
class A
# body
end
# Number 2
A = Class.new
# body
end
但是我注意到当我尝试从类级方法设置类变量时,这两种语法会产生不同的结果。
使用第一种语法,变量在类本身上按预期设置。
irb(main):001:0> class A
irb(main):002:1> def self.foo
irb(main):003:2> @@a = :banana
irb(main):004:2> end
irb(main):005:1> end
=> :foo
irb(main):006:0> A.class_variables
=> []
irb(main):007:0> A.foo
=> :banana
irb(main):008:0> A.class_variables
=> [:@@a]
irb(main):009:0> A.class_variable_get :@@a
=> :banana
irb(main):010:0> Class.class_variable_get :@@a
NameError: uninitialized class variable @@a in Class
from (irb):10:in `class_variable_get'
from (irb):10
from /usr/local/bin/irb:11:in `<main>'
irb(main):011:0>
对于第二个,变量在Class
本身上设置!
irb(main):015:0> K = Class.new do
irb(main):016:1* def self.foo
irb(main):017:2> @@k = :banana
irb(main):018:2> end
irb(main):019:1> end
=> K
irb(main):020:0> K.class_variables
=> []
irb(main):021:0> K.foo
(irb):17: warning: class variable access from toplevel
=> :banana
irb(main):022:0> K.class_variables
=> [:@@k]
irb(main):023:0> K.class_variable_get :@@k
=> :banana
irb(main):024:0> Class.class_variable_get :@@k
=> :banana
为什么会这样?
在我正在进行的工作中,我必须动态生成一个类,所以我别无选择,只能使用第二种语法。因此,如何确保变量@@k
设置在正在定义的新Class
对象上,而不是Class
本身?
答案 0 :(得分:2)
两部分不完全相同,声明的词汇范围存在差异。
在第一个定义中,更常见的类声明,定义在词法上限定为类A
,因此类变量在A上设置。只有A和从A
继承的类才具有类变量。由于Class
不继承自A,因此它无法获取类变量。
在第二个定义中,动态类赋值,定义在词法范围内限定为顶级对象,即对象。因此,类变量将设置在Object
上,即顶级对象的类。
现在,这具有重大意义。每个对象都继承自Object,每个类都是类Class
的一个实例,您在对象空间中的每个类上定义一个类变量。因此,A获取类变量,Class
也获取它。尝试定义一个新类F
,它也会有它。这就是Ruby尖叫警告的原因:
来自toplevel的类变量访问
这是通常避免使用类变量的原因之一。
有多种方法可以解决这个问题。我最喜欢的是,在类实例上使用attr_accessor
:
K = Class.new do
class << self
attr_accessor :fruit
end
self.fruit = :banana
end
K.fruit
# => :banana
# The value isn't shared between inherited classes,
# but the variable is:
class L < K
end
L.fruit
# => nil
L.fruit = :mango
# => :mango
K.fruit
# => :banana
编辑:
请记住,这些变量仍然不是线程安全的,并且由所有线程共享。您将需要线程局部变量来确保线程安全。