我读了“When do Ruby instance variables get set?”,但我在使用类实例变量时有两种想法。
类变量由类的所有对象共享,实例变量属于一个对象。如果我们有类变量,那么使用类实例变量的余地就不大了。
有人可以解释这两者之间的区别以及何时使用它们?
这是一个代码示例:
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
我现在明白了,Class Instance Variables不会传递给继承链!
答案 0 :(得分:241)
类上的实例变量:
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]
类变量:
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
,并且(在需要时)您还可以在整个类层次结构中自动共享。
将这些合并为一个示例,该示例还包含实例上的实例变量:
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
然后在行动中:
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 :(得分:30)
我认为主要(唯一?)不同的是继承:
class T < S
end
p T.k
=> 23
S.k = 24
p T.k
=> 24
p T.s
=> nil
类变量由所有“类实例”(即子类)共享,而类实例变量仅特定于该类。但如果你从不打算扩展你的课程,那么差异纯粹是学术性的。
答案 2 :(得分:22)
#class实例变量仅适用于类方法而不适用于实例方法,而类变量可用于实例方法和类方法。此外,类实例变量在继承链中丢失,而类变量则不会。
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
答案 3 :(得分:13)
正如其他人所说,类变量在给定的类及其子类之间共享。类实例变量恰好属于一个类;它的子类是分开的。
为什么会出现这种行为?好吧,Ruby中的所有东西都是对象偶数类。这意味着每个类都有一个类Class
的对象(或者更确切地说,是Class
的子类)。 (当你说class Foo
时,你真的声明一个常量Foo
并为它分配一个类对象。)并且每个Ruby对象都可以有实例变量,所以类对象可以有实例变量,太
问题是,类对象上的实例变量实际上并不像您通常希望类变量的行为那样。您通常希望在超类中定义的类变量与其子类共享,但这不是实例变量的工作方式 - 子类具有自己的类对象,并且该类对象具有自己的实例变量。因此,他们引入了单独的类变量以及您更可能想要的行为。
换句话说,类实例变量有点像Ruby的设计意外。你可能不应该使用它们,除非你明确知道它们是你正在寻找的东西。
答案 4 :(得分:1)
对于那些具有C ++背景的人,您可能会对与C ++等效语言的比较感兴趣:
class S
{
private: // this is not quite true, in Ruby you can still access these
static int k = 23;
int s = 15;
public:
int get_s() { return s; }
static int get_k() { return k; }
};
std::cerr << S::k() << "\n";
S instance;
std::cerr << instance.s() << "\n";
std::cerr << instance.k() << "\n";
如我们所见,k
是类似static
的变量。这是100%的全局变量,只是它是该类所有的(正确的是 scoped )。这样可以更轻松地避免名称相似的变量之间发生冲突。像任何全局变量一样,该变量只有一个实例,对其进行修改始终对所有人可见。
另一方面,s
是对象特定的值。每个对象都有其自己的值实例。在C ++中,必须创建一个实例才能访问该变量。在Ruby中,类定义本身就是类的实例(在JavaScript中,这称为原型),因此您可以从类中访问s
,而无需其他实例化。可以修改类实例,但是s
的修改将特定于每个实例(每个类型为S
的对象)。因此,修改一个不会改变另一个的值。
答案 5 :(得分:0)
尽管使用类实例变量似乎立即有用,因为类实例变量在子类之间共享,并且可以在单例方法和实例方法中引用它们,但存在一个明显的缺点。它们是共享的,因此子类可以更改类实例变量的值,并且基类也将受到更改的影响,这通常是不受欢迎的行为:
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_attribute。顾名思义,它声明了一个类级别的属性,其值可被子类继承。与类实例变量一样,可以在单例方法和实例方法中访问class_attribute值。但是,在Rails中使用class_attribute的巨大好处是子类可以更改其自身的值,并且不会影响父类。
class C
class_attribute :c
self.c = 'c'
end
C.c
=> "c"
class D < C
end
D.c = 'd'
=> "d"
C.c
=> "c"
答案 6 :(得分:0)
Official Ruby FAQ: What is the difference between class variables and class instance variables?
主要区别在于与继承有关的行为:类变量在类及其所有子类之间共享,而类实例变量仅属于一个特定的类。
在某种程度上,类变量可以看作是继承层次结构中的全局变量,而所有问题都与全局变量有关。例如,一个类变量可能(偶然地)由其任何子类重新分配,从而影响所有其他类:
class Woof
@@sound = "woof"
def self.sound
@@sound
end
end
Woof.sound # => "woof"
class LoudWoof < Woof
@@sound = "WOOF"
end
LoudWoof.sound # => "WOOF"
Woof.sound # => "WOOF" (!)
或者,以后可以重新打开和更改祖先类,可能会产生令人惊讶的效果:
class Foo
@@var = "foo"
def self.var
@@var
end
end
Foo.var # => "foo" (as expected)
class Object
@@var = "object"
end
Foo.var # => "object" (!)
因此,除非您完全知道自己在做什么,并且明确需要这种行为,否则最好使用类实例变量。