2017-10-18 9 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 

ここでは、2つのインスタンスメソッドは、異なる動作:

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はメソッドよりもはるかに複雑なアルゴリズムを使って定数を解決します。定数ルックアップルーチンの1つの段階では、定義のためにスーパークラスチェーンを調べます。このフェーズは、メソッドのルックアップルーチンに非常によく似ていて、なぜメソッドと定数が異なるのかという疑問が、質問に示されている方法でどのように異なっているかを深く示しています。

2つのスーパークラスチェーンルーチンは、開始点のどこが異なるか、つまりどのクラスがチェーンのルートであるかの説明です。

メソッドルックアップは、selfのクラスから始まります。selfは元のメソッド呼び出しの受信者です。この例では、受信者がsub_class_instance、参照が開始されるのは​​です。​​はfoo_methodを実装しているので、すべて正常です。

定数の場合、定数の呼び出しは受信者に関連しないため、Rubyは受信者を参照しません。代わりに、定数スーパークラスルックアップは、定数呼び出しが発生するレキシカルスコープで開いているクラスから始まります。この例では、開かれているクラスはMyClassなので、Rubyが定数を検索し始め、それが見つからないクラスです。

+1

「字句的に外向きに、そして動的に上向き」として要約できると思いますが、IIRCでは実際にはそうではない例が見つかりました。私は彼らが何だったのか覚えていない。 YuguiはいったんRubyのさまざまな暗黙のコンテキストとルックアップに関する一連の記事を書いていましたが、残念ながら、定数ルックアップに関する記事で作業していると言われていましたが、 –

+0

私はYARVを掘り始めたときにどのくらい複雑な定数検索が驚いていましたか? Rubyの滑らかなインターフェースは、ぎっしりとした実装の結果です! "ナチュラルではなく、簡単です" – hoffm

関連する問題