2012-07-09 49 views
0

將塊傳遞給instance_eval時,它意味着在該實例的上下文中執行。 self,當在該塊中顯式或隱式地引用時,應該引用instance_eval已被調用的實例。這似乎在所有情況下都可以正常工作,除了傳遞已轉換爲proc的方法對象時。在這種情況下,self引用方法定義的實例,而不是塊評估的實例。這裏有一個代碼示例來演示我的意思:與instance_eval的行爲不一致

class A 
    def test(&b) 
    instance_eval(&b) 
    end 
end 

class B 
    def test_a(a) 
    a.test { puts self } 
    end 

    def test_b_helper(*args) 
    puts self 
    end 

    def test_b(a) 
    m = method(:test_b_helper).to_proc 
    a.test(&m) 
    end 
end 

a = A.new 
b = B.new 

b.test_a(a) #<A:0x007ff66b086c68> 
b.test_b(a) #<B:0x007fa3e1886bc0> 

預期的行爲是爲兩個測試返回相同的輸出。在這種情況下,self應該指的是A的一個實例,而不是B.

我查看了文檔並完成了一些搜索,但是我一直未能找到有關此特性的信息。我希望有一些經驗豐富的Rubyist能夠幫助理清這種行爲差異。

爲了澄清,我使用的是Ruby 1.9.2。

回答

3

區別在於,Blocks和Procs是關閉,Method對象不是。

摘錄自D.Flanagan,Y.Matsumoto。 The Ruby Programming Language,O'Reilly 2008,p。 204:

一個Method對象和Proc對象之間的重要區別是 該方法的對象不是倒閉。 Ruby的方法旨在使得 是完全獨立的,並且他們永遠無法訪問本地範圍之外的變量 。因此, Method對象所保留的唯一約束是self的值 - 調用 方法的對象。

當您現在施放Method對象to_proc,你的self的值綁定到B的呼叫實例,因此你讓你上述結果。實際上,當您創建Method對象時,已經修復了self

當你考慮下面的代碼這得到特別清楚:

class A 
    def foo 
    puts 'bar' 
    end 
end 

class B; end 

class C < A; end 

foo = A.instance_method(:foo) 
# => #<UnboundMethod: A#foo> 

a = A.new 
foo.bind(a).call 
# bar 

b = B.new 
foo.bind(b).call 
# TypeError: bind argument must be an instance of A 

c = C.new 
foo.bind(c).call 
# bar 

簡單地說:self總是固定在MethodUnboundMethod對象。

+0

@tabdulla:在我的回答中添加了一個清晰的示例。 – 2012-07-11 08:13:03