Доступ к переменным класса Ruby с помощью class_eval и instance_eval - PullRequest
22 голосов
/ 08 августа 2010

У меня есть следующее:

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.

Ответы [ 4 ]

17 голосов
/ 27 августа 2010

Я только что задал тот же вопрос Матцу во время вечеринки RubyKaigi.Я был наполовину пьян, но он был совершенно трезв, поэтому вы можете принять это как окончательный ответ.

Антон прав - причина, по которой вы не можете получить доступ к переменным класса с помощью instance_eval (), - «просто потому что».Даже class_eval () имеет одну и ту же проблему (сам Мэтц не был полностью уверен в class_eval (), пока я не сказал ему, что уже попробовал).Более конкретно: с точки зрения области видимости, переменные класса больше похожи на константы, чем на переменные экземпляра, поэтому переключение self (как это делают instance_eval () и class_eval ()) не имеет никакого значения, когда дело доходит до доступа к ним.

В целом, было бы неплохо вообще избегать переменных класса.

16 голосов
/ 28 августа 2010

РЕДАКТИРОВАТЬ: ниже код был протестирован с 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 в рубине 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

Интересен также случай констант :

Константы * +1051 * class_eval в рубине 1,8 class Hello Foo = :foo end Hello.class_eval { Foo } #=> uninitialized constant class_eval в рубине 1,9 Hello.class_eval { Foo } #=> :foo Таким образом, как и в случае с переменными класса, константы ищутся в 1,9, но не в 1,8 для class_eval instance_eval в рубине 1,8 Hello.instance_eval { Foo } #=> uninitialized constant Hello.new.instance_eval { Foo } #=> uninitialized constant instance_eval в рубине 1,9 Hello.instance_eval { Foo } #=> uninitialized constant Hello.new.instance_eval { Foo } #=> :foo Похоже, что поиск констант не совсем аналогичен поиску переменных классов для Ruby 1.9. Hello экземпляр получает доступ к константе, а класс Hello - нет.

5 голосов
/ 12 августа 2010

Что ж, вероятно, лучший ответ «просто потому что»: 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 ...
4 голосов
/ 14 марта 2014

Рубин 2,1

Это наиболее лаконичный и семантически правильный способ доступа к переменной класса:

class Hello
    @@foo = :foo_value
end

Hello.class_variable_get :@@foo  #=> :foo_value
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...