Різниця між змінними класу та змінними екземпляра класу?


Відповіді:


151

Змінна класу ( @@) ділиться між класом та всіма його нащадками. Змінна екземпляра класу ( @) не належить нащадкам класу.


Змінна класу ( @@)

Давайте матимемо клас Foo зі змінною класу @@iта аксесуари для читання та запису @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

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

end

І похідний клас:

class Bar < Foo
end

Ми бачимо, що Foo і Bar мають однакове значення для @@i:

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

І зміна @@iв одному змінює його в обох:

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

Змінна екземпляра класу (@ )

Давайте створимо простий клас із змінною екземпляра класу @iта засобами доступу для читання та запису @i:

class Foo

  @i = 1

  def self.i
    @i
  end

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

end

І похідний клас:

class Bar < Foo
end

Ми бачимо, що хоча Bar успадковує засоби доступу для @i, він не успадковує @iсебе:

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

Ми можемо встановити бар, @iне впливаючи на Фу @i:

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

1
Навіщо повертати змінні екземпляра за допомогою методів класу? Ви часто стикаєтесь із такою ситуацією?
sekmo

1
@sekmo Аксесуари в цих прикладах повертають або змінні екземпляра класу , які належать до класу, або змінні класу , які належать до ієрархії класів. Вони не повертають звичайні змінні екземпляра, які належать до екземплярів класу. Терміни "змінна екземпляра", "екземпляр класу змінний" та "змінна класу" досить заплутані, чи не так?
Уейн Конрад

72

Спочатку ви повинні розуміти, що класи теж є екземплярами - екземплярами Classкласу.

Як тільки ви це зрозумієте, ви зможете зрозуміти, що клас може мати змінні екземпляра, пов'язані з ним, так само, як і звичайний (читай: некласовий) об'єкт.

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

Зверніть увагу , що змінна екземпляра на Helloце ніяк не пов'язано з і в відміну від змінної примірника на екземпляр зHello

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

З іншого боку, змінна класу є своєрідною комбінацією згаданих двох, оскільки вона доступна як для Helloсебе, так і для її екземплярів, а також для підкласів Helloта їх екземплярів:

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

Багато людей кажуть, щоб уникати class variablesчерез дивну поведінку вище, і рекомендують використовувати class instance variablesзамість цього.


2
+1 Супер пояснення! Це те, що повинен засвоїти кожен програміст, який новачок у Ruby.
TomZ

0

Також я хочу додати, що ви можете отримати доступ до змінної класу ( @@) з будь-якого екземпляра класу

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

Але ви не можете зробити те ж саме для змінної екземпляра класу ( @)

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

-1: Це неправильно: до змінних екземпляра класу можна отримати доступ з кожного екземпляра цього класу (за умови, що доступні присутні). Крім того, ваш приклад показує звичайні змінні екземпляра, а не змінні екземпляра класу. Змінні примірника класу оголошуються поза методами і принципово відрізняються як від звичайних змінних примірника, так і від змінних класу.
The Pellmeister

Як ви можете отримати доступ до змінних екземпляра класу з кожного екземпляра цього класу?
Еван Росс

Чи читали ви відповідь Уейна Конрада? Це буквально це пояснює. stackoverflow.com/a/3803089/439592
Pellmeister

Я знаю, що ви можете отримати доступ за допомогою методів класів, таких як Foo.i, але як ви можете зробити те ж саме для кожного екземпляра цього класу, як Foo.new.i?
Еван Росс

За допомогою #class. Я особисто вважаю, що #classзвички, коли до них selfзвертаються до чого-небудь, але є запахом коду. Ви навіть можете піти на крок далі і застосувати примірники екземплярів iі i=делегувати їх #classеквіваленти, і в цьому випадку ви можете це зробити Foo.new.i. Я не рекомендую робити це, оскільки це створює заплутаний інтерфейс, припускаючи, що ви змінюєте член об’єкта.
The Pellmeister,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.