类实例变量通常比类变量更有用且不那么令人惊讶,因此您可能应该使用它们。
现在,我将详细说明您对 StackOverflow 上的 Ruby 答案所期望的令人痛苦的细节:
要声明或引用类实例变量,您需要在类范围内并使用单个 @ 符号。这会将变量放在该类的单例类实例上。
不幸的是,当您在类范围内并使用def
关键字时,您的方法将放在该类的实例方法列表中,并在实例范围内执行,因此它们的 @-sign 变量将在它们所在的实例上.
相反,我们想要的是在类上定义方法,而不是在它的实例上。这真正意味着这些方法在类对象的单例类的实例方法列表中。(呸!)
所以,总结一下:在类对象的单例类上切换和定义方法至少有四种习语Foo
:
class Foo
@a, @b, @c, @d = 1, 2, 3, 4
# 1. pass the target to def
def Foo.a
@a
end
# 2. pass the target to def, relying on the fact that self
# happens to be the class object right now
def self.b
@b
end
# switch from class scope to singleton class scope
class << self
# 3. define a plain accessor in singleton class scope
def c
@c
end
# 4. use a macro to define an accessor
attr_reader :d
end
end
p [Foo.a, Foo.b, Foo.c, Foo.d]
#=> [1, 2, 3, 4]
(可能还有六种方法可以做到这一点,一旦你考虑到class_eval
等等define_method
,但现在应该让你满意。:-))
最后一点:类实例变量只能通过定义它们的类获得。如果您尝试从(或通过)子类调用这些方法中的任何一个,这些方法将执行,但 @ 变量将全部为零,因为self
将是子类的类对象,而不是父类的类对象。
class Bar < Foo
end
p [Bar.a, Bar.b, Bar.c, Bar.d]
#=> [nil, nil, nil, nil]