Ruby instance_eval для класса с помощью attr_accessor
Я понимаю принципиальную разницу между instance_eval
а также class_eval
, Однако то, что я обнаружил, когда играю, - это нечто странное attr_accessor
, Вот пример:
A = Class.new
A.class_eval{ attr_accessor :x }
a = A.new
a.x = "x"
a.x
=> "x" # ... expected
A.instance_eval{ attr_accessor :y }
A.y = "y"
=> NoMethodError: undefined method `y=' for A:Class
a.y = "y"
=> "y" # WHATTT?
Как это так:
- instance_eval не был у метода доступа к нашему классу A (объекту)
- затем он фактически добавил его на экземпляры A?
4 ответа
Во-первых, ваше понимание (или интуиция) правильно, методы определены внутри #instance_eval
а также #class_eval
не одинаковы
A = Class.new
A.instance_eval { def defined_in_instance_eval; :instance_eval; end }
A.class_eval { def defined_in_class_eval; :class_eval; end }
A.new.defined_in_class_eval # => :class_eval
A.defined_in_instance_eval # => :instance_eval
примечание: пока self
одинаково в обоих instance_eval
а также class_eval
определение по умолчанию другое, см. http://yugui.jp/articles/846
Что на самом деле делает трюк Module#attr_accessor
Сам посмотри на его определение: http://rxr.whitequark.org/mri/source/vm_method.c#620
он не использует def
не читает контекст, self
или определение по умолчанию. Он просто "вручную" вставляет методы в модуль. Вот почему результат нелогичен.
Для разницы между class_eval
а также instance_eval
см. Динамически создаваемый метод класса
class A; end
A.class_eval do
attr_accessor :x
def barx; end
define_method :foox do; end
end
print 'A.instance_methods : '; p A.instance_methods(false).sort
print 'A.singleton_methods : '; p A.singleton_methods
class B; end
B.instance_eval do
attr_accessor :y
def bary; end
define_method :fooy do; end
end
print 'B.instance_methods : '; p B.instance_methods(false).sort
print 'B.singleton_methods : '; p B.singleton_methods
class C; end
singleton_class = class << C; self end
singleton_class.instance_eval do
attr_accessor :z
def barz; puts 'where is barz ?' end
define_method :fooz do; end
end
print 'C.instance_methods : '; p C.instance_methods(false).sort
print 'C.singleton_methods : '; p C.singleton_methods
print 'singleton_class.barz : '; singleton_class.barz
print 'singleton_class.methods : '; p singleton_class.methods(false)
Вывод (ruby 1.8.6):
A.instance_methods : ["barx", "foox", "x", "x="]
A.singleton_methods : []
B.instance_methods : ["fooy", "y", "y="]
B.singleton_methods : ["bary"]
C.instance_methods : []
C.singleton_methods : ["z", "z=", "fooz"]
singleton_class.barz : where is barz ?
singleton_class.methods : ["barz"]
Как вы можете видеть с B, несмотря на то, что instance_eval обычно создает одноэлементные методы, очевидно, attr_accessor
а также define_method
форсировать определение методов экземпляра.
A.singleton_class.class_eval { attr_accessor :y }
A.y = 'y'
A.y
Метод attr_accessor
это метод класса, такой, что при вызове в теле класса методы доступа определяются в экземплярах этого класса.
Когда вы делаете A.class_eval{...}
, вы называете это в теле класса A
так что его экземпляры, такие как a
назначены аксессоры.
Когда вы делаете A.instance_eval{...}
, вы называете это в не-теле класса A
таким образом, его экземпляры не назначены методы доступа.
Если вы делаете Class.class_eval{attr_accessor :z}
тогда вы называете это внутри тела класса Class
так что его экземпляры, такие как A
будут назначены аксессоры: A.z = ...
,