Różnica między zmiennymi klas a zmiennymi instancji klas?

Odpowiedzi:

152

Zmienna klasy ( @@) jest wspólna dla klasy i wszystkich jej potomków. Zmienna instancji klasy ( @) nie jest współużytkowana przez potomków klasy.


Zmienna klasy (@@ )

Miejmy klasę Foo ze zmienną klasy @@ii akcesoriami do czytania i pisania @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

  def self.i=(value)
    @@i = value
  end

end

I klasa pochodna:

class Bar < Foo
end

Widzimy, że Foo i Bar mają tę samą wartość dla @@i:

p Foo.i    # => 1
p Bar.i    # => 1

A zmiana @@iw jednym zmienia to w obu:

Bar.i = 2
p Foo.i    # => 2
p Bar.i    # => 2

Zmienna instancji klasy ( @)

Stwórzmy prostą klasę ze zmienną instancji klasy @ii akcesoriami do czytania i pisania @i:

class Foo

  @i = 1

  def self.i
    @i
  end

  def self.i=(value)
    @i = value
  end

end

I klasa pochodna:

class Bar < Foo
end

Widzimy, że chociaż Bar dziedziczy akcesory dla @i, nie dziedziczy @isiebie:

p Foo.i    # => 1
p Bar.i    # => nil

Możemy ustawić słupki @ibez wpływu na Foo @i:

Bar.i = 2
p Foo.i    # => 1
p Bar.i    # => 2
Wayne Conrad
źródło
1
Po co zwracać zmienne instancji przy użyciu metod klas? Czy często spotykasz się z taką sytuacją?
sekmo
1
@sekmo Metody dostępu w tych przykładach zwracają albo zmienne instancji klasy , które należą do klasy, albo zmienne klas , które należą do hierarchii klas. Nie zwracają zwykłych zmiennych instancji, które należą do instancji klasy. Pojęcia „zmienna instancji”, „zmienna instancji klasy” i „zmienna klasy” są dość mylące, prawda?
Wayne Conrad
72

Najpierw musisz zrozumieć, że klasy to także instancje - instancje Classklasy.

Kiedy już to zrozumiesz, zrozumiesz, że klasa może mieć przypisane zmienne instancji, tak jak zwykły obiekt (czytaj: nieklasowy).

Hello = Class.new

# setting an instance variable on the Hello class
Hello.instance_variable_set(:@var, "good morning!")

# getting an instance variable on the Hello class
Hello.instance_variable_get(:@var) #=> "good morning!"

Zauważ, że zmienna instancji on Hellojest całkowicie niezwiązana ze zmienną instancji w instancji i różni się od niej wHello

hello = Hello.new

# setting an instance variable on an instance of Hello
hello.instance_variable_set(:@var, :"bad evening!")

# getting an instance variable on an instance of Hello
hello.instance_variable_get(:@var) #=> "bad evening!")

# see that it's distinct from @var on Hello
Hello.instance_variable_get(:@var) #=> "good morning!"

Z drugiej strony zmienna klasowa jest rodzajem kombinacji powyższych dwóch, ponieważ jest dostępna w Hellosobie i jej instancjach, a także w podklasachHello i ich instancjach:

HelloChild = Class.new(Hello)
Hello.class_variable_set(:@@class_var, "strange day!")
hello = Hello.new
hello_child = HelloChild.new

Hello.class_variable_get(:@@class_var) #=> "strange day!"
HelloChild.class_variable_get(:@@class_var) #=> "strange day!"
hello.singleton_class.class_variable_get(:@@class_var) #=> "strange day!"
hello_child.singleton_class.class_variable_get(:@@class_Var) #=> "strange day!"

Wiele osób mówi, aby unikać z class variablespowodu powyższego dziwnego zachowania i zaleca użycie class instance variableszamiast.

koński
źródło
2
+1 super wyjaśnienie! To jest coś, co powinien przetrawić każdy nowy programista Ruby.
TomZ
0

Chcę również dodać, że możesz uzyskać dostęp do zmiennej klasy ( @@) z dowolnej instancji klasy

class Foo
  def set_name
    @@name = 'Nik'
  end

  def get_name
    @@name
  end
end


a = Foo.new
a.set_name
p a.get_name # => Nik
b = Foo.new
p b.get_name # => Nik

Ale nie możesz zrobić tego samego dla zmiennej instancji klasy ( @)

class Foo
  def set_name
    @name = 'Nik'
  end

  def get_name
    @name
  end
end


a = Foo.new
a.set_name
p a.get_name # => Nik
b = Foo.new
p b.get_name # => nil
Evan Ross
źródło
-1: To jest niepoprawne: do zmiennych instancji klasy można uzyskać dostęp z każdej instancji tej klasy (pod warunkiem, że istnieją metody dostępu). Poza tym Twój przykład pokazuje zwykłe zmienne instancji, a nie zmienne instancji klas. Zmienne instancji klas są deklarowane poza metodami i zasadniczo różnią się od zwykłych zmiennych instancji i zmiennych klas.
Pellmeister
Jak uzyskać dostęp do zmiennych instancji klasy z każdej instancji tej klasy?
Evan Ross,
Czytałeś odpowiedź Wayne'a Conrada? To dosłownie to wyjaśnia. stackoverflow.com/a/3803089/439592
The Pellmeister
Wiem, że możesz uzyskać dostęp z metod klasowych Foo.i, ale jak możesz zrobić to samo dla każdego wystąpienia tej klasy, jak Foo.new.i?
Evan Ross
Używając #class. Osobiście uważam, że #classzwyczaje wywoływane czegokolwiek, ale selfsą to zapachy kodu. Możesz nawet pójść o krok dalej i zaimplementować metody dostępu do instancji ii i=delegować je na ich #classodpowiedniki, w takim przypadku możesz to zrobić Foo.new.i. Nie polecam tego robić, ponieważ tworzy to mylący interfejs, co sugeruje, że modyfikujesz element członkowski obiektu.
The Pellmeister