2015-12-06 8 views
13

die folgende Klasse Gegeben:Warum können geschützte Methoden nicht mit dem Symbol proc aufgerufen werden?

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

    def b 
    dup.tap(&:bar) 
    end 

    protected 

    def bar 
    puts 'bar' 
    end 
end 

Es scheint, wie sowohl Foo#a und Foo#b gleichwertig sein sollten, aber sie sind nicht:

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

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

Gibt es einen Grund dafür? Ist das ein Fehler?

Ruby Getestet 2.2.3p173

+1

Das ist eine gute Frage. Hoffen wir, dass uns jemand aufklären kann. –

+0

Gute Frage in der Tat.Der einzige Unterschied, den ich sehen kann, ist, dass Sie 'bar' für eine lokale Variable im ersten Block aufrufen, aber keine Variable mit einem proc aufrufen. Trotzdem, neugierig, ob es dafür einen guten Grund gibt. – DaniG2k

+0

Getestet ohne die 'foo' Variable, aber immer noch die gleiche Sache. Ich denke, das muss damit zu tun haben, wie Procs aufgerufen werden, obwohl ich mir vorstellen würde, dass diese beiden Methoden äquivalente Ergebnisse ausgeben sollten. – DaniG2k

Antwort

3

Start Lassen Sie sich mit der Feststellung, dass in Ruby, wie Sie wahrscheinlich wissen, in einem Verfahren a auf einer Klasse deklariert Foo, ich Methoden auf jede Instanz vonFoo geschützt aufrufen kann.

Wie ermittelt Ruby, ob wir in einer Klasse Klasse Foo sind? Um das zu verstehen, müssen wir uns in die Interna der Methodenaufrufe vertiefen. Ich werde Beispiele aus der Version 2.2 der MRT verwenden, aber vermutlich sind das Verhalten und die Implementierung in anderen Versionen die gleichen (ich würde aber gerne die Ergebnisse sehen, wenn ich das auf JRuby oder Rubinious teste).

Ruby tut dies in rb_call0. Wie der Kommentar andeutet, wird self verwendet, um zu bestimmen, ob wir geschützte Methoden aufrufen können. self wird in rb_call von den Call-Frame-Informationen des aktuellen Threads abgerufen. Dann überprüfen wir in rb_method_call_status, dass dieser Wert self von derselben Klasse ist, für die eine geschützte Methode definiert ist.

Blöcke verwirren das Problem etwas. Denken Sie daran, dass lokale Variablen in einer Ruby-Methode von jedem in dieser Methode deklarierten Block erfasst werden. Das bedeutet, dass self im Block self ist, für den die Methode aufgerufen wurde. Lassen Sie uns ein Beispiel an:

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 

dieses Rennen, sehen wir die gleiche Instanz von Foo auf allen Ebenen die gleiche, auch wenn wir die proc aus einem völlig anderen Objekt aufgerufen.

So, jetzt verstehen wir, warum es möglich ist, eine geschützte Methode für eine andere Instanz der gleichen Klasse innerhalb eines Blocks in einer Methode aufzurufen.

Nun schauen wir uns an, warum ein mit &:bar erstellter Prozess das nicht kann. Wenn wir ein & Zeichen vor einem Methodenargument platzieren, machen wir zwei Dinge: ruby ​​anweisen, dieses Argument als Block zu übergeben und es anweisen, to_proc darauf anzurufen.

Dies bedeutet, dass die Methode Symbol#to_proc aufgerufen wird. Diese Methode ist in C implementiert, aber wenn wir eine C-Methode aufrufen, wird der Zeiger auf self auf dem aktuellen Frame der Empfänger dieser C-Methode - in diesem Fall wird es das Symbol :bar. Wir betrachten also die Instanz foo, die wir haben , als ob wir in einer Methode der Klasse Symbol sind, und wir können eine geschützte Methode nicht aufrufen.

Das ist ein Bissen, aber hoffentlich macht es genug Sinn. Lassen Sie mich wissen, wenn Sie Vorschläge haben, wie ich es verbessern könnte!

Verwandte Themen