Доступ к переменным класса Ruby Class с Class_eval и instance_eval
-
26-09-2019 - |
Вопрос
У меня есть следующее:
class Test
@@a = 10
def show_a()
puts "a: #{@@a}"
end
class << self
@@b = '40'
def show_b
puts "b: #{@@b}"
end
end
end
Почему работает следующее:
Test.instance_eval{show_b}
b: 40
=> nil
Но я не могу получить доступ к @@b
напрямую?
Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object
Аналогично, следующие работы
t = Test.new
t.instance_eval{show_a}
a: 10
=> nil
Но следующее не удалось
t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object
Я не понимаю, почему я не могу получить доступ к переменным класса напрямую от instance_eval
блоки.
Решение
Я только что спросил тот же вопрос к Matz во время партии Рубикииджи. Я был наполовину пьян, но он был совершенно трезвым, поэтому вы можете взять это как окончательный ответ.
Антон прав - причина, по которой вы не можете получить доступ к переменным класса через Issue_eval () - это «только потому, что». Даже Class_eval () делится ту же проблему (сам Мату не был полностью уверен в Class_eval (), пока я не сказал ему, что я уже попробовал). Более конкретно: Scope-Wise, переменные классов больше похожи на константы, чем переменные экземпляра, поэтому переключение Self (Ascone_eval () и Class_eval () do) не собирается иметь никакого значения, когда речь идет о доступе.
В общем, это может быть хорошая идея, чтобы полностью избежать переменных классов.
Другие советы
РЕДАКТИРОВАТЬ: Ниже код был протестирован с 1.8.7 и 1.9.1 ... Кажется, ситуация снова отличается с 1.9.2: /
Ситуация на самом деле не так просто. Есть различия в поведении в зависимости от того, используете ли вы 1,8 или 1,9 и используете ли вы class_eval
или instance_eval
.
Приведенные ниже примеры детали поведение в большинстве ситуаций.
Я также включал поведение констант для хорошей меры, поскольку их поведение похоже на, но не совсем так же, как, переменные классов.
Переменные классов
class_eval
В Ruby 1.8:
class Hello
@@foo = :foo
end
Hello.class_eval { @@foo } #=> uninitialized class variable
class_eval
в Ruby 1.9:
Hello.class_eval { @@foo } #=> :foo
Таким образом, переменные классов являются посмотрел вверх в 1.9 (но не в 1.8) при использовании class_eval
instance_eval
в Ruby 1.8 а также 1.9
Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable
Похоже, что классы переменные нет посмотрел вверх в 1.8 или 1.9 при использовании instance_eval
Что также интересно, имеет место для константы:
Константы
class_eval
в Ruby 1.8
class Hello
Foo = :foo
end
Hello.class_eval { Foo } #=> uninitialized constant
class_eval
в Ruby 1.9
Hello.class_eval { Foo } #=> :foo
Итак, как с переменными класса, константы выглядят вверх в 1.9, но нет В 1.8 для class_eval
instance_eval
в Ruby 1.8
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant
instance_eval
в Ruby 1.9
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo
Похоже, что постоянный поиск не совсем аналогичен переменную класса посмотреть вверх для Ruby 1.9. А. Hello
пример делает получить доступ к константу во время Hello
класс не делает.
Ну, наверное, лучший ответ - «просто потому, что»: Exmance_eval в двух словах создает какой-то тип Singleton, который вызывается с привязкой данного объекта. Я согласен, что звучит немного странно, но это то, что оно есть.
Если вы выполняете Exstance_eval со строкой, вы даже получите предупреждение о том, что ваш метод пытается получить доступ к переменной класса:
irb(main):038:0> Test.new.instance_eval "@@a"
(eval):1: warning: class variable access from toplevel singleton method
NameError: (eval):1:in `irb_binding': uninitialized class variable ...
Ruby 2.1
Это самый краткий и семантически правильный способ получить доступ к переменной классу:
class Hello
@@foo = :foo_value
end
Hello.class_variable_get :@@foo #=> :foo_value