2013-01-20 79 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 
相關問題