У меня есть следующее:
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
.
Я только что задал тот же вопрос Матцу во время вечеринки RubyKaigi. Я был полупьяным, но он был совершенно трезв, так что можете принять это как окончательный ответ.
Антон прав - причина, по которой вы не можете получить доступ к переменным класса через instance_eval(), "просто потому что". Даже class_eval() разделяет ту же проблему (сам Матц не был полностью уверен в class_eval(), пока я не сказал ему, что уже пробовал). Более конкретно: с точки зрения области действия переменные класса больше похожи на константы, чем на переменные экземпляра, поэтому переключение self (как это делают instance_eval() и class_eval()) не будет иметь никакого значения, когда дело доходит до доступа к ним.
Вообще, было бы неплохо вообще избегать переменных класса.
Что ж, вероятно, лучший ответ - «просто потому, что»: instance_eval в двух словах создает своего рода одноэлементный процесс, который вызывается с привязкой данного объекта. Я согласен, это звучит немного странно, но это то, что есть.
Если вы выполните instance_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 ...
РЕДАКТИРОВАТЬ: приведенный ниже код был протестирован с 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
— нет.