2017-10-18 179 views
1

考慮下面的代碼:爲什麼Ruby不能找到調用者類中定義的常量?

class MyClass 
    def foo_via_method 
    foo_method 
    end 

    def foo_via_constant 
    FOO_CONSTANT 
    end 
end 

class SubClass < MyClass 
    FOO_CONSTANT = "foo" 

    def foo_method 
    FOO_CONSTANT 
    end 
end 

兩個實例此方法的行爲是不同的:

sub_class_instance = SubClass.new 

### THIS WORKS ### 
sub_class_instance.foo_via_method 
# => "foo" 

### THIS DOESN'T ### 
sub_class_instance.foo_via_constant 
# NameError: uninitialized constant MyClass::FOO_CONSTANT 

引用的方法在子類中的版本返回所需的值,而是指版本子類中的常量會引發錯誤。所以難題是這樣的:爲什麼使用方法的版本可以工作,但使用常量的版本會失敗?

+0

這對我來說是一個難以理解的設計。在OOP中,我永遠不會期望能夠查找在子類中定義的某些東西,除非(我的理解是對此的價值存在爭議)一種行爲被覆蓋的方法。當然,你永遠不應該期望只有在子類中定義的東西才能被父類看到。 –

+0

來自我:使用方法的版本 - 在類SubClass的範圍內調用常量,以便在另一個函數中調用'FOO_CONSTANT',它試圖在MyClass的範圍內找到這個常量,以使其可行:寫'SubClass :: FOO_CONSTANT' –

回答

2

這是我在實際生產代碼中遇到的一個難題。我寫了一篇關於this blog post正在發生什麼的詳細說明。

以下是TLDR:Ruby使用更復雜的算法來解析常量,而不是使用方法。常量查找例程的一個階段涉及查看超類鏈中的定義。這個階段看起來非常像方法查找例程,加深了爲什麼方法和常量在問題中說明的方式不同的奧祕。

解釋是兩個超類鏈例程在它們的起始開始時不同,即哪個類是鏈的根。

方法查找以self的類開始,其中self是原始方法調用的接收方。在該示例中,sub_class_instance是接收器,​​是查找開始的位置。​​執行foo_method,所以一切都很好。

對於常量,Ruby不會引用接收者,因爲常量調用與接收者不相關。相反,常量超類查找從在常量調用發生的詞法作用域打開的類開始。在該示例中,已打開的類是MyClass,因此Ruby開始查找常量並從未找到它。

+1

我認爲它可以概括爲「詞彙向外,然後動態向上」,但是IIRC我找到了實際上並不成立的例子。不過,我不記得他們是什麼人。 Yugui曾經寫過一系列有關Ruby中各種隱式上下文和查找的文章,但不幸的是,雖然她提到了一篇關於不斷查找的文章,但該文章從未出現過。 –

+0

當我開始深入YARV時,我驚訝地發現複雜的不斷查找。 Ruby的漂亮界面是一些粗糙實現的結果! 「自然,不簡單。」 – hoffm

相關問題