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


Відповіді:


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.