我想详细说明其他人所说的话,特别是比较类变量的使用与类实例变量。让我们从这个开始:
class Holder1
end
class Holder2 < Holder1
@@var = 99
# Create a class setter and a getter for the class variable
def Holder2.var=(val)
@@var = val
end
def Holder2.var
@@var
end
# Create a instance getter for the class variable
def var
@@var
end
end
class Holder3 < Holder2
end
Holder2.var #=> 99
Holder2.var = 1
Holder3.var #=> 1
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder2.new.var #=> 1
Holder3.new.var #=> 1
Holder3.var = 2
Holder3.var #=> 2
Holder2.var #=> 2
Holder3.new.var #=> 2
Holder2.new.var #=> 2
另外:前两种方法通常会这样写:
def self.var=(val)
@@var = val
end
def self.var
@@var
end
这是有效的,因为 self => Holder2 在创建方法时。通过不硬连线类名,如果您决定重命名类,则不需要更改。
现在这(使用类变量)可能正是您想要的行为。也就是说,如果您希望子类看到变量并能够更改它,那么您需要一个类变量。
但是,如果您希望每个子类都有自己的变量,子类既不能看到也不能更改该变量,则需要使用 类实例变量,@var,而不是类变量,@@var。 (从技术上讲,这并不完全正确,因为可以在程序中的任何位置使用 Holder2.instance_variable_get(:@var) 或 Holder2.instance_variable_set(:@var)。)
将下面代码的结果与上面的结果进行比较。我包含了一个与类实例变量@var 同名的实例变量,以说明它们与@night 和@day 一样不同。
class Holder1
end
class Holder2 < Holder1
# Create an accessor for the instance variable
attr_accessor :var
# Initialize class instance variable
@var = 99
# Create an accessor for the class instance variable
class << self
puts "self in 'class << self': #{self}"
attr_accessor :var
def our_dog
"Diva"
end
end
# Create a class method
def self.our_cat
puts "self in 'self.our_cat())' def: #{self}"
"Teagan"
end
# Create an instance setter and a getter for the class instance variable
def c_ivar=(val)
self.class.var = val
end
def c_ivar
self.class.var
end
end
class Holder3 < Holder2
end
#=> self in 'class << self': #<Class:Holder2>
Holder2.var #=> 99
h2 = Holder2.new
h2.var #=> nil
Holder2.var = 1
Holder2.var #=> 1
h2.c_ivar #=> 1
h2.c_ivar = 2
Holder2.var #=> 2
h2.var #=> nil
h2.var = 3
h2.var #=> 3
Holder2.var #=> 2
Holder3.var #=> nil
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder3.var = 4
Holder3.var #=> 4
Holder2.var #=> 2
h3 = Holder3.new
h3.c_ivar #=> 4
h2.c_ivar #=> 2
Holder3.our_dog #=> "Diva"
Holder3.our_cat #=> "self in 'self.our_cat())' def: Holder3"
#=> "Teagan"
Holder1.var 引发异常,因为该类无权访问Holder2 的类实例变量var@。相比之下,第一次使用 Holder3.var 会返回 nil,因为通过继承存在 Holder3 的变量,但尚未初始化。
表达式class << self 将self 从Holder2 更改为Holder2 的单例类(又名元类),直到下一个end。请注意,Ruby 将Holder2 的单例类表示为#<Class:Holder2>。此外,我们不需要在my_dog 前面加上self.,因为self 在创建方法时是Holder2 的单例类。
注意:
Holder2.singleton_methods #=> [:var, :var=, :our_dog, :our_cat]
这表明our_cat() 是Holder2 的单例类的方法,即使self 是Holder2(而不是Holder2 的单例类',#<Class:Holder2>)。建。出于这个原因,有人说,从技术上讲,"there is no such thing as a class method"。这也告诉我们,我们可以将my_cat 的定义移到class << self 构造中(并删除self.)。
另一种向Holder2的单例类添加实例变量和方法的常用方法是将class << self ... end替换为extend M并创建一个模块:
module M
attr_accessor :var
def our_dog
"Diva"
end
end
Object#extend 将这些实例变量和方法混合到Holder2 的单例类中。