2014-03-28 52 views
1

當我使用include C時,我非常困惑爲什麼模塊B和C中的方法混入模塊A中的類中。是否以某種方式遞歸地混合名稱空間下的所有類?爲什麼Ruby包含污染不相關的類?

module A 
    class AClass 
    def a_method_ 
    end 
    end 
end 

module B 
    extend self 
    def b_method_ 
    end 
end 

module C 
    extend self 
    include B 
    def c_method_ 
    puts A::AClass.new.methods.grep /_method_/ 
    end 
end 

C.c_method_ 

puts "----" 
include C 
c_method_ 

有了結果,之前包括,在ACLASS實例正確只有方法a_method_,但畢竟有它可以訪問其他兩種方法爲好。

a_method_ 
---- 
a_method_ 
c_method_ 
b_method_ 

有人可以幫忙解釋一下嗎?

回答

1

如果你改變你的代碼的末尾這樣:

C.c_method_ 

puts self.inspect 
puts "----" 
include C 
c_method_ 

你應該幫你看看這是怎麼回事。這會給你這樣的輸出:

a_method_ 
main 
---- 
a_method_ 
c_method_ 
b_method_ 

那麼,什麼是這個main的公司嗎? Chuckthis answer有一個不錯的總結:

Ruby中的一切都發生在一些對象的上下文中。頂層的對象稱爲「main」。它基本上是一個具有特殊屬性的Object實例,其中定義的所有方法都添加爲Object的實例方法(因此它們在任何地方都可用)。

這意味着,在頂層這樣說:

include C 

是一樣的話說:

Object.send(:include, C) 

添加的東西Object污染的一切。

+0

這很有道理!我認爲把'include B'放在'module C'中並不污染'class A'的事實讓我感到困惑。我不明白爲什麼當它放在'main'上下文中時遞歸地工作,但當嵌入到模塊中時卻沒有。 –

1

您可能會發現這個照明:

puts Object.instance_methods.grep /_method_/ 
# c_method_ 
# b_method_ 

你的方法已經被添加到的一切,不只是AClass!並且要真正證明這種奇怪,請嘗試將最後一位更改爲:

class D 
    include C 
    puts Object.instance_methods.grep /_method_/ 
end 

沒有更多輸出!你已經偶然發現了Ruby的mainweird, intentional behaviors之一(當沒有其他指定時,隱式接收器)。其基本動機是,如果你定義主要的方法,你要能夠在任何地方調用它:

def foo 
end 
# now all of these work: 
class A 
    foo 
end 
module B 
    foo 
end 
A.new.instance_eval { foo } 
A.singleton_class.class_eval { foo } 

這不是關於範圍分辨率要麼;如果您將其更改爲foo = :bar,您將獲得NameError s。這是用於像putsgets這樣的東西,當你打電話給他們時,你通常不關心接收者是誰。但是,這種行爲並沒有與Ruby的對象模型完全混淆,所以matz黑客入侵:main中定義的每個方法都被添加到Object作爲實例方法。由於一切都是一個對象,其餘的都按預期工作。

相關問題