Ruby sınıf örneği değişkeni ile sınıf değişkeni


179

" Ruby örnek değişkenleri ne zaman ayarlanır? "

Sınıf değişkenleri bir sınıfın tüm nesneleri tarafından paylaşılır, Örnek değişkenleri bir nesneye aittir. Sınıf değişkenlerimiz varsa sınıf örneği değişkenlerini kullanmak için çok fazla alan kalmadı.

Birisi bu ikisi arasındaki farkı ve ne zaman kullanılacağını açıklayabilir mi?

İşte bir kod örneği:

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

Şimdi anlıyorum, Class Instance Değişkenleri miras zincirinden geçmiyor!

Yanıtlar:


276

Sınıftaki örnek değişken:

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]

Sınıf değişkeni:

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]

Bir sınıftaki örnek değişkeni ile (bu sınıfın bir örneğinde değil), alt sınıflara otomatik olarak sahip olmadan (ve tersi) o sınıf için ortak bir şey depolayabilirsiniz. Sınıf değişkenleri ile, self.classbir örnek nesneden yazmak zorunda kalmama rahatlığına sahip olursunuz ve (istenirse) sınıf hiyerarşisinde otomatik paylaşım elde edersiniz.


Bunları, örneklerdeki örnek değişkenleri de kapsayan tek bir örnekte birleştirmek:

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

Ve sonra eylemde:

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] 

@Phronz Kodda bahsettiğiniz self.things ve self.class.things arasındaki fark nedir?
cyborg

1
@cyborg self.thingsbaşvurulan bir yöntem olup things, (bu örneğinin yöntemi olacak bir sınıfın bir örneği durumunda) geçerli kapsam içinde burada self.class.thingsreferanslar thingsgeçerli kapsam sınıfından yöntemi (yine bir sınıfın bir örneği durumunda, bunun anlamı sınıf yöntemi).
graffzon

Güzel açıklama.
aliahme922

30

Ana (sadece?) Farklı miras olduğuna inanıyorum:

class T < S
end

p T.k
=> 23

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

p T.s
=> nil

Sınıf değişkenleri tüm "sınıf örnekleri" (yani alt sınıflar) tarafından paylaşılırken, sınıf örneği değişkenleri yalnızca bu sınıfa özgüdür. Ancak sınıfınızı asla genişletmeyi düşünmüyorsanız, fark tamamen akademiktir.


1
Tek fark bu değil. "Paylaşılan" ve "örnek" yalnızca mirastan daha ileri gider. Örnek alıcıları koyarsanız S.new.s => nilve alırsınız S.new.k => 23.
Andre Figueiredo

27

Kaynak

Örnek yöntemlerinin kullanılabilirliği

  • Sınıf örneği değişkenleri, yalnızca sınıf yöntemleri için kullanılabilir, örnek yöntemleri için kullanılamaz.
  • Sınıf değişkenleri hem örnek yöntemleri hem de sınıf yöntemleri tarafından kullanılabilir.

kalıtsallığı

  • Sınıf örneği değişkenleri miras zincirinde kaybolur.
  • Sınıf değişkenleri değildir.
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

15

Diğerlerinin de söylediği gibi, sınıf değişkenleri belirli bir sınıf ve alt sınıfları arasında paylaşılır. Sınıf örneği değişkenleri tam olarak bir sınıfa aittir; alt sınıfları ayrıdır.

Bu davranış neden var? Ruby'deki her şey bir nesne, hatta sınıflar. Bu, her sınıfın kendisine karşılık gelen Classbir sınıf nesnesine (veya daha doğrusu bir alt sınıfına) sahip olduğu anlamına gelir Class. (Söylediğinizde class Foo, gerçekten bir sabit Foobildiriyorsunuz ve ona bir sınıf nesnesi ataıyorsunuz.) Ve her Ruby nesnesinin örnek değişkenleri olabilir, bu nedenle sınıf nesnelerinin de örnek değişkenleri olabilir.

Sorun şu ki, sınıf nesnelerindeki örnek değişkenler, genellikle sınıf değişkenlerinin davranışını istediğiniz şekilde davranmaz. Genellikle üst sınıfta tanımlanan bir sınıf değişkeninin alt sınıflarıyla paylaşılmasını istersiniz, ancak örnek değişkenlerin çalışma şekli bu değildir; alt sınıfın kendi sınıf nesnesi vardır ve bu sınıf nesnesinin kendi örnek değişkenleri vardır. Bu nedenle, daha olası olma olasılığınız olan ayrı sınıf değişkenleri tanıttılar.

Başka bir deyişle, sınıf örneği değişkenleri Ruby'nin tasarımının bir kazasıdır. Özellikle aradıklarınızı bilmedikçe muhtemelen kullanmamalısınız.


sınıf değişkeni Java statik değişken gibi?
Kick Buttowski

3

Resmi Ruby SSS: Sınıf değişkenleri ile sınıf örneği değişkenleri arasındaki fark nedir?

Temel fark kalıtımla ilgili davranıştır: sınıf değişkenleri bir sınıf ve tüm alt sınıfları arasında paylaşılırken sınıf örneği değişkenleri sadece belirli bir sınıfa aittir.

Sınıf değişkenleri bir şekilde miras hiyerarşisi bağlamında global değişkenler olarak görülebilir ve küresel değişkenlerle gelen tüm problemler. Örneğin, bir sınıf değişkeni (yanlışlıkla) alt sınıflarından herhangi biri tarafından yeniden atanarak diğer tüm sınıfları etkileyebilir:

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

Ya da, bir ata sınıfı daha sonra yeniden açılabilir ve değiştirilebilir, muhtemelen şaşırtıcı etkileri vardır:

class Foo

  @@var = "foo"

  def self.var
    @@var
  end
end

Foo.var  # => "foo" (as expected)

class Object
  @@var = "object"
end

Foo.var  # => "object" (!)

Yani, ne yaptığınızı tam olarak bilmiyorsanız ve bu tür davranışlara açıkça ihtiyacınız yoksa, sınıf örneği değişkenlerini kullanmanız daha iyi olur.


2

C ++ arka planı olanlar için, C ++ eşdeğeriyle bir karşılaştırmayla ilgilenebilirsiniz:

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";

Gördüğümüz gibi, kbir olan staticdeğişken gibi. Bu, küresel bir değişken gibi% 100'dür, ancak sınıfa ait olması dışında ( doğru olduğu kapsamlıdır ). Bu, benzer şekilde adlandırılmış değişkenler arasındaki çakışmaların önlenmesini kolaylaştırır. Herhangi bir global değişken gibi, bu değişkenin sadece bir örneği vardır ve bu değişkenin değiştirilmesi her zaman herkes tarafından görülebilir.

Öte yandan, snesneye özgü bir değerdir. Her nesnenin kendi değer örneği vardır. C ++ 'da, bu değişkene erişim için bir örnek oluşturmanız gerekir. Ruby'de sınıf tanımının kendisi sınıfın bir örneğidir (JavaScript'te buna prototip denir), bu nedenle sek örnekleme olmadan sınıftan erişebilirsiniz . Sınıf örneği değiştirilebilir, ancak modifikasyonu sher örneğe (türün her nesnesi S) özgü olacaktır . Bu nedenle, birini değiştirmek diğerinin değerini değiştirmeyecektir.


1

Sınıf örneği değişkenlerini kullanmak hemen faydalı gibi görünse de, sınıf örneği değişkeni alt sınıflar arasında paylaşıldığından ve hem tekil hem de örnek yöntemlerinde atıfta bulunulabildiğinden, tek bir dezavantaj vardır. Paylaşılırlar ve böylece alt sınıflar sınıf örneği değişkeninin değerini değiştirebilir ve temel sınıf da genellikle istenmeyen davranış olan değişiklikten etkilenir:

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 adlı kullanışlı bir yöntem sunar. Adından da anlaşılacağı gibi, değeri alt sınıflar tarafından devralınabilen sınıf düzeyinde bir özellik bildirir. Class_attribute değerine, sınıf örneği değişkeninde olduğu gibi, hem tekton hem de örnek yöntemlerinde erişilebilir. Bununla birlikte, Rails içindeki class_attribute ile elde edilen en büyük fayda, alt sınıfların kendi değerlerini değiştirebilmesidir ve ana sınıfı etkilemez.

class C
  class_attribute :c
  self.c = 'c'
end

 C.c
 => "c" 

class D < C
end

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

 C.c
 => "c" 

Güzel ara, bunu daha önce kullanmamıştım. Bu self.özelliğe her erişmek istediğinizde başa geleceğinden emin olmanız gerekir, ancak çalışıyor gibi görünüyor c, örn self.c. Dokümanlar bir default:parametrenin iletilebileceğini söylüyor , class_attributeancak az önce bahsettiğim noktadan dolayı işe yaramıyor gibi görünüyor self.
Dex

"Sınıf örneği değişkenlerini kullanmak hemen yararlı görünse de" derseniz, "sınıf örneği değişkenleri değil" demek "sınıf değişkenleri" demek istediğinizi düşünüyorum (Bkz. Ruby-lang.org/en/documentation/faq/8/). )
Keith Bennett

Evet, bu cevap, sorunun tüm noktası olan "sınıf örneği değişkenlerini" ve "sınıf değişkenlerini" tamamen karıştırmaktadır.
stevo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.