我理解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?
怎么回事:
答案 0 :(得分:7)
首先,您的理解(或直觉)是正确的,#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
相同,但默认定义不同,请参阅{{3 }}
真正的诀窍是Module#attr_accessor
本身,看看它的定义:
http://yugui.jp/articles/846
它不使用def
,它不读取上下文,self
或默认的definee。它只是“手动”将方法插入到模块中。这就是结果违反直觉的原因。
答案 1 :(得分:2)
有关class_eval
和instance_eval
之间的差异,请参阅Dynamically creating class method
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)
输出(红宝石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
强制定义实例方法。
答案 2 :(得分:1)
A.singleton_class.class_eval { attr_accessor :y }
A.y = 'y'
A.y
答案 3 :(得分:0)
方法attr_accessor
是一种类方法,当在类的 body 中调用时,则上定义了访问器方法该类的实例。
执行A.class_eval{...}
时,您在类 A
的正文中调用它,因此其实例例如a
被分配了访问者。
执行A.instance_eval{...}
后,您在类 A
的非正文内调用它,因此其实例未分配访问者
如果您执行Class.class_eval{attr_accessor :z}
,那么您使用类 Class
的正文来调用它,因此其实例< / strong>例如A
将被分配访问者:A.z = ...
。