2015-12-06 4 views
13

は、以下のクラスを考える:保護されたメソッドをsymbol to procで呼び出すことができないのはなぜですか?

class Foo 
    def a 
    dup.tap { |foo| foo.bar } 
    end 

    def b 
    dup.tap(&:bar) 
    end 

    protected 

    def bar 
    puts 'bar' 
    end 
end 

同等であるべき両方Foo#aFoo#bのように思えるが、しかし、彼らはありませんよ。その理由は

> Foo.new.a 
bar 
=> #<Foo:0x007fe64a951ab8> 

> Foo.new.b 
NoMethodError: protected method `bar' called for #<Foo:0x007fe64a940a88> 

ありますか?これはバグですか?あなたはおそらく知っているようにRubyの2.2.3p173

+1

これは大きな質問です。誰かが私たちを啓発できることを祈りましょう。 –

+0

本当に良い質問です。私が見ることのできる違いは、最初のブロックのローカル変数で 'bar'を呼び出すだけですが、procで変数を指定しないと呼び出すことができます。それでも良い理由があるのか​​どうかは不思議です。 – DaniG2k

+0

'foo'変数なしでテストされていますが、それでも同じことがあります。私は、これらの2つのメソッドが同等の結果を出力する必要があるとは思いますが、これはprocsの呼び出し方法と関係があります。 – DaniG2k

答えて

3

でテスト

は、Rubyでいることに注目することによって開始レッツ、クラスFooで宣言方法aでは、私がFooの任意のインスタンスを保護されたメソッドを呼び出すことができます。

私たちがクラスFooで宣言されたメソッドにいるかどうかをRubyがどのように判断しますか?これを理解するには、メソッド呼び出しの内部を掘り下げなければなりません。私はMRIのバージョン2.2からの例を使用しますが、おそらく動作と実装は他のバージョンでも同じです(私はこれをJRubyやRubiniousでテストした結果を見たいと思っています)。

Rubyはrb_call0でこれを行います。コメントに示されているように、selfを使用して、保護されたメソッドを呼び出すことができるかどうかを判断します。 selfは、現在のスレッドの呼び出しフレーム情報からrb_callに取得されます。次にrb_method_call_statusでは、この値selfが保護されたメソッドが定義されている同じクラスであることを確認します。

ブロックは問題を多少混乱させます。 Rubyメソッドのローカル変数は、そのメソッドで宣言されたブロックによって取得されることに注意してください。これは、ブロック内で、selfがメソッドが呼び出された同じselfであることを意味します。例を見てみましょう:

class Foo 
    def give_me_a_block! 
     puts "making a block, self is #{self}" 
     Proc.new do 
      puts "self in block0 is #{self}" 
     end 
    end 
end 

proc = Foo.new.give_me_a_block! 

proc.call 

がこれを実行すると、我々はFooの同じインスタンスは、我々は完全に異なるオブジェクトからPROCと呼ばれるにもかかわらず、すべてのレベルで同じである参照してください。

これで、同じクラスの別のインスタンスで保護されたメソッドをメソッド内のブロック内から呼び出すことができる理由を理解しました。

ここで、&:barで作成されたprocがこれを行うことができない理由を見てみましょう。メソッドの引数の前に&の符号を付けるときは、2つのことを行います:rubyにこの引数をブロックとして渡し、to_procを呼び出すように指示します。

これは、メソッドSymbol#to_procを呼び出すことを意味します。このメソッドはCで実装されていますが、Cメソッドを呼び出すと、現在のフレームのselfへのポインタがそのCメソッドの受信者になります。この場合は、シンボル:barになります。だから我々はfooのインスタンスを見ているのですが、のように、Symbolクラスのメソッドであるかのようになり、保護されたメソッドを呼び出すことはできません。

これは一口ですが、うまくいけば十分です。どのように私がそれを改善するかもしれないかについての提案があれば教えてください!

関連する問題