2010-07-02 73 views
20

根據文檔mod.const_get(sym)「返回mod中命名常量的值」。在Ruby中混淆const_get的行爲?

我也知道const_get默認可以查找接收器的繼承鏈。所以下面的工作:

class A; HELLO = :hello; end 
class B < A; end 
B.const_get(:HELLO) #=> :hello 

我也知道,班Ruby的子類Object,這樣就可以使用const_get查找「全局」常量,即使接收器是一個普通的類:

class C; end 
C.const_get(:Array) #=> Array 

但是,這是我困惑的地方 - 模塊不能繼承Object。那麼爲什麼我仍然可以使用const_get從模塊中查找'全局'常量?爲什麼以下工作?

module M; end 
M.const_get(:Array) #=> Array 

如果文檔是正確的 - const_get只需查找接收器或其超下定義的常量。但是在上面的代碼中,Object不是M的超類,那麼爲什麼可以查找Array

感謝

+3

請注意,這與'::'的行爲不匹配。 'SomeModule :: SomeGlobalConstant'會導致錯誤,而'SomeModule。const_get(:SomeGlobalConstant)'會工作。 – sepp2k 2010-07-02 12:16:52

回答

11

你是正確的混淆... doc文件沒有說明,使紅寶石的特殊情況爲常數的查找在Modules和已修改to state this explicitly。如果在常規層次結構中找不到常量,則Ruby將重新啓動從Object開始的查找,可能是found in the source

不斷查找本身可能有點混亂。看看下面的例子:

module M 
    Foo = :bar 
    module N 
    # Accessing Foo here is fine: 
    p Foo # => bar 
    end 
end 
module M::N 
    # Accessing Foo here isn't 
    p Foo # => uninitialized constant M::N::Foo 
end 
p M::N.const_get :Foo # => uninitialized constant M::N::Foo 

在這兩個地方,不過,訪問Object級別的常量像Array是罰款(感謝上帝!)。發生了什麼事是Ruby維護着一個「打開的模塊定義」列表。如果一個常量有一個明確的範圍,比如說LookHereOnly::Foo,那麼只有LookHereOnly及其包含的模塊將被搜索。如果未指定範圍(如上面示例中的Foo),則Ruby將查看已打開的模塊定義,以查找常數FooM::N,然後M,最後是Object。最上面打開的模塊定義總是Object

所以當打開類僅M::NObject,就像在我的例子的最後一部分M::N.const_get :Foo相當於訪問Foo

我希望我得到這個權利,怎麼我還通過不斷查找自己弄得:-)

+1

任何想法爲什麼這樣做?在哪些情況下會有用? – sepp2k 2010-07-02 18:43:18

+0

@ sepp2k:不確定它是否是_useful_,但我試着解釋我認爲它背後的邏輯。 – 2010-07-03 05:09:00

+0

這不直觀,但M :: N問題是由於詞法範圍。如果你做'module M;模塊N;結束;結尾「時,M的內容處於N的詞彙範圍內(由於嵌套,M從N可見)。用'module M :: N;結束「,因爲包含範圍是頂級,所以M不可見。就我個人而言,我儘量避免在M :: N格式中定義類和模塊 - 也因爲如果M還不存在,這是一個錯誤。 – Kelvin 2012-01-04 00:00:35

2

我想出了下面的腳本加載名稱間隔常量:

def load_constant(name) 
    parts = name.split('::') 
    klass = Module.const_get(parts.shift) 
    klass = klass.const_get(parts.shift) until parts.empty? 
    klass 
end 
0

只要因爲我們沒有檢查錯誤,因此您可以:

def load_constant(name) 
    name.split('::').inject(Module) do |mod_path, mod_to_find| 
     mod_path.const_get(mod_to_find) 
    end 
end