Ruby类实例变量与类变量

Ruby class instance variable vs. class variable

我读到"Ruby实例变量什么时候设置?"但是在使用类实例变量时我有两个想法。

类变量由类的所有对象共享,实例变量属于一个对象。如果我们有类变量,就没有足够的空间使用类实例变量。

有人能解释一下这两者的区别以及何时使用它们吗?

下面是一个代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
class S
  @@k = 23
  @s = 15
  def self.s
    @s
  end
  def self.k
     @@k
  end

end
p S.s #15
p S.k #23

我现在明白了,类实例变量不是沿着继承链传递的!


类上的实例变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Parent
  @things = []
  def self.things
    @things
  end
  def things
    self.class.things
  end
end

class Child < Parent
  @things = []
end

Parent.things << :car
Child.things  << :doll
mom = Parent.new
dad = Parent.new

p Parent.things #=> [:car]
p Child.things  #=> [:doll]
p mom.things    #=> [:car]
p dad.things    #=> [:car]

类变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Parent
  @@things = []
  def self.things
    @@things
  end
  def things
    @@things
  end
end

class Child < Parent
end

Parent.things << :car
Child.things  << :doll

p Parent.things #=> [:car,:doll]
p Child.things  #=> [:car,:doll]

mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new

[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]

通过类上的实例变量(不是该类的实例),您可以存储该类的公共内容,而无需子类自动获取它们(反之亦然)。使用类变量,您可以不必从实例对象写入self.class,并且(如果需要)还可以在整个类层次结构中自动共享。

将这些合并到一个示例中,该示例还包括实例上的实例变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Parent
  @@family_things = []    # Shared between class and subclasses
  @shared_things  = []    # Specific to this class

  def self.family_things
    @@family_things
  end
  def self.shared_things
    @shared_things
  end

  attr_accessor :my_things
  def initialize
    @my_things = []       # Just for me
  end
  def family_things
    self.class.family_things
  end
  def shared_things
    self.class.shared_things
  end
end

class Child < Parent
  @shared_things = []
end

然后付诸行动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new

Parent.family_things << :house
papa.family_things   << :vacuum
mama.shared_things   << :car
papa.shared_things   << :blender
papa.my_things       << :quadcopter
joey.my_things       << :bike
suzy.my_things       << :doll
joey.shared_things   << :puzzle
suzy.shared_things   << :blocks

p Parent.family_things #=> [:house, :vacuum]
p Child.family_things  #=> [:house, :vacuum]
p papa.family_things   #=> [:house, :vacuum]
p mama.family_things   #=> [:house, :vacuum]
p joey.family_things   #=> [:house, :vacuum]
p suzy.family_things   #=> [:house, :vacuum]

p Parent.shared_things #=> [:car, :blender]
p papa.shared_things   #=> [:car, :blender]
p mama.shared_things   #=> [:car, :blender]
p Child.shared_things  #=> [:puzzle, :blocks]  
p joey.shared_things   #=> [:puzzle, :blocks]
p suzy.shared_things   #=> [:puzzle, :blocks]

p papa.my_things       #=> [:quadcopter]
p mama.my_things       #=> []
p joey.my_things       #=> [:bike]
p suzy.my_things       #=> [:doll]


我相信主要(只是?)不同的是继承:

1
2
3
4
5
6
7
8
9
10
11
12
class T < S
end

p T.k
=> 23

S.k = 24
p T.k
=> 24

p T.s
=> nil

类变量由所有"类实例"(即子类)共享,而类实例变量仅特定于该类。但是,如果你从未打算延长你的课程,这完全是学术上的差异。


#类实例变量仅可用于类方法而不可用于实例方法,而类变量可用于实例方法和类方法。此外,类实例变量在继承链中丢失,而类变量则不丢失。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Vars

  @class_ins_var ="class instance variable value"  #class instance variable
  @@class_var ="class variable value" #class  variable

  def self.class_method
    puts @class_ins_var
    puts @@class_var
  end

  def instance_method
    puts @class_ins_var
    puts @@class_var
  end
end

Vars.class_method

puts"see the difference"

obj = Vars.new

obj.instance_method

class VarsChild < Vars


end

VarsChild.class_method

正如其他人所说,类变量在给定的类及其子类之间共享。类实例变量只属于一个类;它的子类是独立的。

为什么会有这种行为?好吧,Ruby中的所有内容都是对象,甚至是类。这意味着每个类都有一个对应于它的类Class的对象(或者更确切地说,是Class的子类)。(当你说class Foo时,你实际上是在声明一个常量Foo并为它分配一个类对象。)每个ruby对象都可以有实例变量,因此类对象也可以有实例变量。

问题是,类对象上的实例变量的行为并不像您通常希望类变量的行为那样。您通常希望在超类中定义的类变量与其子类共享,但这不是实例变量的工作方式,子类有自己的类对象,而类对象有自己的实例变量。因此,他们引入了不同的类变量,这些变量具有您更可能想要的行为。

换句话说,类实例变量是Ruby设计的一个意外。你可能不应该使用它们,除非你明确知道它们是你要找的。


虽然使用类实例变量似乎很有用,但是由于类实例变量在子类之间共享,并且可以在单例方法和实例方法中引用,因此存在一个单独的缺点。它们是共享的,因此子类可以更改类实例变量的值,而基类也会受到更改的影响,这通常是不需要的行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class C
  @@c = 'c'
  def self.c_val
    @@c
  end
end

C.c_val
 =>"c"

class D < C
end

D.instance_eval do
  def change_c_val
    @@c = 'd'
  end
end
 => :change_c_val

D.change_c_val
(irb):12: warning: class variable access from toplevel
 =>"d"

C.c_val
 =>"d"

Rails引入了一种称为class_属性的简便方法。顾名思义,它声明了一个类级属性,其值可以由子类继承。类属性值可以在singleton和instance方法中访问,类实例变量也是如此。然而,rails中class_属性的巨大好处是子类可以改变自己的值,并且不会影响父类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class C
  class_attribute :c
  self.c = 'c'
end

 C.c
 =>"c"

class D < C
end

D.c = 'd'
 =>"d"

 C.c
 =>"c"