带有attr_accessor的类上的Ruby instance_eval

时间:2013-01-20 19:46:54

标签: ruby attr-accessor class-eval instance-eval

我理解instance_evalclass_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?

怎么回事:

  1. instance_eval不在访问者的A类(对象)
  2. 然后将其添加到A?
  3. 的实例中

4 个答案:

答案 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

附注:虽然selfinstance_evalclass_eval相同,但默认定义不同,请参阅{{3 }}

真正的诀窍是Module#attr_accessor本身,看看它的定义: http://yugui.jp/articles/846

它不使用def,它不读取上下文,self或默认的definee。它只是“手动”将方法插入到模块中。这就是结果违反直觉的原因。

答案 1 :(得分:2)

有关class_evalinstance_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_accessordefine_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 = ...