web-dev-qa-db-ja.com

Rubyクラスインスタンス変数とクラス変数

Rubyインスタンス変数はいつ設定されますか? 」と読みますが、クラスインスタンス変数を使用するタイミングは2つあります。

クラス変数はクラスのすべてのオブジェクトで共有され、インスタンス変数は1つのオブジェクトに属します。クラス変数がある場合、クラスインスタンス変数を使用する余地はあまりありません。

誰かがこれら2つの違いとそれらをいつ使用するかを説明できますか?

コード例を次に示します。

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

私は今、クラスインスタンス変数が継承チェーンに沿って渡されないことを理解しています!

165
Elmor

クラスのインスタンス変数:

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を記述する必要がなく、(必要な場合)クラス階層全体で自動的に共有することもできます。


これらを1つの例にマージして、インスタンスのインスタンス変数もカバーします。

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] 
243
Phrogz

私は主な(唯一の?)違いは継承だと信じています:

class T < S
end

p T.k
=> 23

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

p T.s
=> nil

クラス変数はすべての「クラスインスタンス」(つまりサブクラス)で共有されますが、クラスインスタンス変数はそのクラスのみに固有です。しかし、クラスを拡張するつもりがない場合、違いは純粋にアカデミックです。

30
bioneuralnet

#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
22
Sachin Saxena

他の人が言ったように、クラス変数は特定のクラスとそのサブクラスの間で共有されます。クラスインスタンス変数は、正確に1つのクラスに属します。そのサブクラスは別個です。

なぜこの動作が存在するのですか?さて、Rubyのすべてはオブジェクト、さらにはクラスです。つまり、各クラスには、対応するクラスClass(または、Classのサブクラス)のオブジェクトがあります。 (あなたがclass Fooと言うとき、あなたは本当に定数Fooを宣言し、それにクラスオブジェクトを割り当てています。)そして、すべてのRubyオブジェクトはインスタンス変数を持つことができるので、クラスオブジェクトもインスタンス変数を持つことができます。

問題は、クラスオブジェクトのインスタンス変数が、クラス変数の通常の動作とは異なることです。通常、スーパークラスで定義されたクラス変数をそのサブクラスと共有する必要がありますが、それはインスタンス変数の仕組みではありません。サブクラスには独自のクラスオブジェクトがあり、そのクラスオブジェクトには独自のインスタンス変数があります。そこで、彼らはあなたが望んでいる可能性が高い動作を持つ個別のクラス変数を導入しました。

つまり、クラスインスタンス変数は、Rubyの設計の一種の偶然です。探しているものであることが明確にわかっていない限り、おそらく使用しないでください。

13

クラスインスタンス変数はサブクラス間で共有され、シングルトンメソッドとインスタンスメソッドの両方で参照できるため、クラスインスタンス変数を使用するとすぐに役立つように思えるかもしれませんが、重大な欠点があります。これらは共有されているため、サブクラスはクラスインスタンス変数の値を変更でき、基本クラスも変更の影響を受けます。これは通常、望ましくない動作です。

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" 
0
Donato

公式Ruby FAQ:クラス変数とクラスインスタンス変数の違いは何ですか?

主な違いは、継承に関する動作です。クラス変数はクラスとそのすべてのサブクラスの間で共有されますが、クラスインスタンス変数は1つの特定のクラスにのみ属します。

クラス変数は、何らかの方法で、継承階層のコンテキスト内のグローバル変数と見なすことができ、グローバル変数に伴うすべての問題があります。たとえば、クラス変数はそのサブクラスのいずれかによって(偶然に)再割り当てされ、他のすべてのクラスに影響を与える可能性があります。

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" (!)

そのため、自分が何をしているかを正確に知り、この種の動作を明示的に必要としない限り、クラスインスタンス変数を使用することをお勧めします。

0
not a patch