使用类变量
假设一个简单的ruby程序 class Holder
@@var = 99
def Holder.var=(val)
@@var = val
end
def var
@@var
end
end
@@var = "top level variable"
a = Holder.new
puts a.var
我猜结果应为99
,但输出不是99
。我想知道为什么。由于类变量的范围是类,我假设行@@var = "top level variable"
不会影响类中的变量。
答案 0 :(得分:6)
@@var
是Holder
的类变量。并且顶级的@@var
与@@var
的同名Holder
的类变量不同,您正在为类Object
创建一个全新的类变量。现在@@var
与父类的子类共享。 Object
是类Holder
的父类。在Ruby中,如果没有显式定义任何自定义类的超类,则使用class
关键字进行定义,然后Object
类成为您刚刚创建的类的默认超类。 / p>
在顶层,您在类Object
中定义了一个新的类变量,就像您在发布的示例中一样。通过继承,它现在可供类Holder
使用。
我的意思是,当你写下面的内容时:
class Holder
@@var = 99
def Holder.var=(val)
@@var = val
end
def var
@@var
end
end
@@var
尚未添加到Object
课程中。现在在写下面一行的顶层:
@@var = "top level variable"
这意味着,您要将其添加到Object
类,并更新旧变量(@@var
)与Holder
类中的相同名称,使用这个新名称,您刚刚定义在Object
class。范围内。
请记住,类变量是共享变量,仅对类(B
)可见,您在其中定义了它及其后代类(C
),后代({ {1}})后代类(D
),依此类推。但是在类(C
如下所示)之前的超类(A
)不可见,直到您在父类中定义了相同的名称变量({ {1}}),就像你在子类(B
)中一样。
A
答案 1 :(得分:1)
我想扩大其他人所说的内容,特别是比较类变量与类实例变量的使用。让我们从这开始:
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
=&gt;创建方法时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
的单例类中。