2013-01-20 39 views
5

我了解instance_evalclass_eval之间的基本区别。我发现虽然玩耍时有些奇怪,但涉及attr_accessor。这里有一个例子:具有attr_accessor的类的Ruby实例_eval

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的情况下, ?

回答

5

起初,你的理解(或直觉)是正确的,里面#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_evalclass_eval,在相同默认definee是不同的,看http://yugui.jp/articles/846

真正的伎俩是Module#attr_accessor本身,看它的定义: http://rxr.whitequark.org/mri/source/vm_method.c#620

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

0

方法attr_accessor是一个类方法,使得在一个类的调用时,则存取方法是在实例该类的定义。

当你做A.class_eval{...},你是一个A内调用它,所以它的实例a分配存取。

当你做A.instance_eval{...},你的A一个非身体内调用它,所以它的情况下未分配存取。

如果你这样做Class.class_eval{attr_accessor :z},那么你就调用它withing一个Class,所以其实例A将被分配存取:A.z = ...。 ,

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的通常会产生这样的事实:

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)单例方法,显然attr_accessordefine_method强制实例方法的定义。

+0

这个人搞砸了。很好的例子。 @ Daniel_Vartanov的答案实际上解释了为什么'attr_accessor'不适用于self,但这是一个很好地说明每种类型的def'n方法会发生什么情况的方法。谢谢一堆。 – brad

0
A.singleton_class.class_eval { attr_accessor :y } 
A.y = 'y' 
A.y 
相关问题