2015-06-12 3 views
1

Когда я использую Class.new, по какой-то причине класс переменных приведенных классов мешают друг другу:класса переменных странное поведение

# ruby 2.1.6p336 (2015-04-13 revision 50298) [i686-linux] 

result = Class.new do 
    p self # #<Class:0xb7cd5624> 

    @@foo = 1 
    def foo 
    p @@foo 
    end 
end 

result2 = Class.new do 
    p self # #<Class:0xb7cd54d0> 

    @@foo = 2 
    def foo 
    p @@foo 
    end 
end 

result.class_variable_set(:@@foo, 3) 
result.new.foo # expected 3, output 3 
result2.new.foo # expected 2, output 3 

Почему? Что происходит под капотом?

Также есть связанные предупреждения, но я не могу понять, что они означают, ни найти хорошее описание.

warning: class variable access from toplevel 

Ближайшие ключи, которые я нашел до сих пор являются:

Доступ к переменной класса считается верхний уровень, потому что класс ключевое слово не определяет имя класса, который обеспечивал бы простор удерживайте переменную класса.

(с) http://japhr.blogspot.ru/2009/06/more-information-about-class-variables.html

Поскольку вы не создаете класс с class ключевым словом, переменная класса создается на Object, не Test

(с) https://stackoverflow.com/a/10712458/1432640

Не могли бы вы подробно описать, почему это происходит и почему оно так отличается от wh ru Я использую ключевое слово class?

+0

Не знаете, почему это было приостановлено? –

ответ

1

Давайте пройдем через него:

result = Class.new do 
    p self # #<Class:0xb7cd5624> 

    @@foo = 1 
    def foo 
    p @@foo 
    end 
end 

На данный момент, result был создан как экземпляр Class и переменной класса для Class, @@foo, устанавливается в 1.

result2 = Class.new do 
    p self # #<Class:0xb7cd54d0> 

    @@foo = 2 
    def foo 
    p @@foo 
    end 
end 

На данный момент result2 был создан как экземпляр Class, а переменная класса для Class, @@foo установлена ​​в 2.

result.class_variable_set(:@@foo, 3) 

Это устанавливает в классе Class в класс переменной с именем @@foo к значению 3.

result.new.foo # expected 3, output 3 

выход 3, поскольку @@foo установлен в 3 из приведенного выше class_variable_set.

result2.new.foo # expected 2, output 3 

result2 уже создан до появления этого утверждения, так @@foo = 2 не выполняются. Конструкция выше для result2 = Class.new ... создала экземпляр уже. result2.new создает новый экземпляр Class, и она осуществляет конструктор Class, не код, который вы выше result2 = Class.new ... (которые уже были выполнены курица вы сконструированный result2).Основание Class constructor не знает о себе @@foo и не использует его и не задает. Таким образом, значение @@foo еще 3.


Что касается предупреждающего сообщения:

предупреждения: переменный класса доступа с верхнего уровнем

вы можете захотеть Google поиска предупредительного сообщения, так как есть несколько хороших ссылок для чтения. В частности, вы можете посмотреть на The many gotchas of Ruby class variables. Эта краткая статья также объясняет, что я описываю выше.


Если вы хотите иметь отдельный переменный класс для новых, динамически создаваемых классов, вы можете создать/установить переменный класс после создаются класс. Вы можете назвать классы, присвоив их константе:

1.9.2-p330 :017 > result.class_variable_set(:@@bar, 3) 
=> 3 
1.9.2-p330 :018 > result2.class_variable_set(:@@bar, 4) 
=> 4 
1.9.2-p330 :019 > R1 = result 
=> R1 
1.9.2-p330 :020 > R2 = result2 
=> R2 
1.9.2-p330 :021 > class R1 
1.9.2-p330 :022?> def bar 
1.9.2-p330 :023?>  p @@bar 
1.9.2-p330 :024?>  end 
1.9.2-p330 :025?> end 
=> nil 
1.9.2-p330 :026 > class R2 
1.9.2-p330 :027?> def bar 
1.9.2-p330 :028?>  p @@bar 
1.9.2-p330 :029?>  end 
1.9.2-p330 :030?> end 
=> nil 
1.9.2-p330 :031 > R1.new.bar 
3 
=> 3 
1.9.2-p330 :032 > R2.new.bar 
4 
=> 4 
1.9.2-p330 :033 > 
+0

_В этом случае результат был создан как экземпляр класса, а переменная класса для класса @@ foo установлена ​​в 1._ Я думаю, что именно здесь я наткнулся. В документах написано: _ Создает новый анонимный (неназванный) класс ... (http://ruby-doc.org/core-1.9.3/Class.html#method-c-new)_ Следовательно, его переменные класса должны принадлежать этому новому анонимному (неназванному) классу, не так ли? – stillwaiting

+0

@stillwaiting Это отличный момент. Хотя анонимный класс по-прежнему является неназванным и анонимным, переменная является общей (часть базового объекта 'Class'). Если вы назвали новый, анонимный класс и создали переменные класса в классах по имени, они будут раздельными. Я обновлю свой ответ, чтобы показать это. – lurker

+0

Спасибо, но не уверен, что я следую. Вы пишете _ «Если вы хотите иметь отдельные переменные класса для ваших новых, динамически созданных классов, вы можете создавать/устанавливать переменные класса после создания класса.» _, Но это то, что я делаю: сначала я создаю экземпляр класса, и после этого я пытаюсь назначить переменную класса.Если я удалю «@@ foo = 1» из блока, ничего не изменится на самом деле, результат все тот же. Ни если я не приведу результат или результат2 в R1 или R2. Я понимаю, что все работает гладко с классом, но это не так, я думаю. Мне что-то не хватает? – stillwaiting

0

Переменные класса разделяются во всей иерархии классов. Возможно, вам понадобится использовать class instance variables для вашего прецедента.

[42] pry(main)> class Parent 
[42] pry(main)* @@foo = "Parent" 
[42] pry(main)* end 

[43] pry(main)> class Thing1 < Parent 
[43] pry(main)* @@foo = "Thing1" 
[43] pry(main)* end 

[44] pry(main)> class Thing2 < Parent 
[44] pry(main)* @@foo = "Thing2" 
[44] pry(main)* end 

[45] pry(main)> Thing1.class_eval("@@foo") 
=> "Thing2" 
[46] pry(main)> Parent.class_eval("@@foo") 
=> "Thing2" 
[47] pry(main)> Class.class_eval("@@foo") 
=> "Thing2" 

Ruby, class variables and inheritance ? super class @@var gets changed?

0

блоков создать новую вложенную лексическую область и близко над окружающими их лексической областью. Они делают, однако, не изменение динамической области.

Динамический объем остается неизменным внутри блока как вне его, то есть значение self, значение дефолтного определения, текущего класса.

Переменные класса на верхнем уровне становятся переменными класса Object. Более того, два блока имеют одинаковую внешнюю область. Поэтому они имеют одни и те же переменные класса.

Обратите внимание, что некоторые методы фактически делают изменить динамический объем блока, например, instance_exec и instance_eval изменить значение self.

+0

Спасибо. Хотите узнать, что такое «динамическая область»? Если я напечатаю «я», все они разные. Снаружи он печатает «главный», внутри первого блока он печатает # <класс: 0xb7cd5624>, во втором он # <класс: 0xb7cd54d0> .... – stillwaiting

Смежные вопросы