Ruby's def и instance_eval против class_eval - PullRequest
26 голосов
/ 10 декабря 2010

Я читаю раздел метапрограммирования Программирование на Ruby 1.9 , и мне трудно понять, что происходит внутри между class_eval / class_exec против instance_eval /instance_exec.

Итак, во-первых, я понимаю, что def добавляет метод к таблице методов self (объект класса):

class A
  puts self  # => A
  def foo; 42; end  # added to the method table of self, so becomes an instance method
end
A.new.foo  # => 42

И если мы используем class_eval, мы получим такое же поведение:

A.class_eval do
  puts self  # => A
  def bar; 42; end  # same as above
end
A.new.bar  # => 42

Но как-то в instance_eval случае все иначе:

A.instance_eval do
  puts self  # => A
  def baz; 42; end  # added to the method table of an anonymous
                    # singleton class of self, so becomes a class method
end
puts A.baz  # => 42

s = 'string'
s.instance_eval do ... end  # same behavior, so now def creates an instance method

Итак, я понимаю функциональную разницу между class_eval и instance_eval.

Но контексты внутри блоков class_eval и instance_eval выглядят для меня точно одинаково - в частности, self указывает на один и тот же объект, а local_variables одинаковы , Так что же происходит внутри (внутри) блоков, что заставляет def действовать иначе?

Есть ли какая-нибудь документация, которую я мог бы прочитать? RDoc для instance_eval и class_eval не помогает. Глядя на источник, instance_eval , кажется, устанавливает объект класса singleton, тогда как class_eval - нет, но видна ли эта разница вне кода C, на уровне Ruby?

Ответы [ 2 ]

33 голосов
/ 10 декабря 2010

Я думаю, что ваше замешательство связано с тем, что def не зависит от текущего «я», вы можете думать о нем как о «текущем классе», который имеет свои собственные правила.

Следуя вашим примерам:

class A
  # defs here go to A
  puts self  # => A
  class << self
     #defs here go to A's eigenclass
  end
end

A.class_eval do
  #defs here go to A
end

A.instance_eval do
  #defs here go to A's eigenclass     
end

s = "Hello World"

class << s
  #defs here go to s's eigenclass
end

Вот часть главы, в которой говорится о проблеме, и довольно ясно о поведении

class_eval и instance_eval устанавливают self на время блока.Однако они отличаются тем, как они настраивают среду для определения метода.class_eval настраивает вещи так, как если бы вы были в теле определения класса, поэтому определения методов будут определять методы экземпляра. Напротив, вызов instance_eval для класса действует так, как если бы вы работали внутри синглтон-класса self.Поэтому любые методы, которые вы определите, станут методами класса.

Единственное, что я думаю, стоит добавить, это то, что вы можете вызывать instance_eval для любого объекта, не только для классов, и поведение не меняется, ноимеет различные последствия.

Некоторые соответствующие чтения:

Ruby: определения метода instance_eval и class_eval

Глава 4 этой наиболее выдающейся серии

3 голосов
/ 10 декабря 2010

Просто добавьте к ответу @ krusty.ar: def и define_method добавьте методы в текущий контекст определения метода (я верю, что это называется, я не уверен), а не к текущему self.

Просто внутри тела модуля, класса или одноэлементного класса эти два оказались одинаковыми.

Но, например, в теле скрипта (он же верхнего уровня) self - это объект main, но текущий контекст определения метода - Object.

...