2015-11-14 5 views
8

Method#unbind gibt einen Verweis auf die Methode UnboundMethod zurück, die später mithilfe von UnboundMethod#bind an ein anderes Objekt gebunden werden kann.Was ist der Zweck von Rubys Methoden zum Auflösen von Methoden?

class Foo 
    attr_reader :baz 

    def initialize(baz) 
    @baz = baz 
    end 
end 

class Bar 
    def initialize(baz) 
    @baz = baz 
    end 
end 

f = Foo.new(:test1) 
g = Foo.new(:test2) 
h = Bar.new(:test3) 
f.method(:baz).unbind.bind(g).call # => :test2 
f.method(:baz).unbind.bind(h).call # => TypeError: bind argument must be an instance of Foo 

Anfangs dachte ich, das unglaublich genial, weil ich es erwartet hätte Function.prototype.call()/Function.prototype.apply() ähnlich wie JavaScript arbeiten. Das Objekt, an das Sie die Methode binden möchten, muss jedoch derselben Klasse angehören.

Die einzige Anwendung, die ich mir vorstellen kann, ist, wenn Sie eine Methode lösen, die ursprüngliche Implementierung verlieren (die Methode in der ursprünglichen oder Singleton-Klasse neu definieren) und dann erneut binden und aufrufen.

+1

fand ich eine ordentliche Erklärung hier - http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html –

+0

@WandMaker, nette Idee. Es fällt in die Kategorie, die ich beschrieben habe. Ich wäre überrascht, wenn dies der einzige Grund für seine Existenz wäre. – ndn

Antwort

0

Methode und UnboundMethod-Typen erwarten, dass das Bindungsziel eine Unterklasse der ursprünglichen Klasse sein muss, in der Sie auf die Methode verwiesen haben. Allerdings hat die Methode eine #to_proc Methode implementiert und damit können Sie die Einschränkung "gleicher Klassenart" loswerden.

Sie müssen #send Methode verwenden, wie #define_method ist privat (Sie können es nicht direkt aufrufen).

class A 
    def hoge ; "hoge" ; end 
end 

class B ; end 

hoge = A.new.method(:hoge) 

B.send(:define_method, :hoge_in_b, &hoge) #converting to proc 

b = B.new 
puts b.hoge_in_b 
+0

Zuerst wird der Block im Kontext des ursprünglichen Objekts ausgewertet (alias das 'A.new'-Objekt, nicht das' B.new'-Objekt). Zweitens beantwortet dies nicht die Frage, worauf es bei der Entbindungsmechanik ankommt. – ndn

+0

Natürlich wird es im Zusammenhang mit A.new ausgewertet, weil es eine "Schließung" ist.Es gibt keine High-Level-Sprache, die Funktionen als First-Class-Objekte verwendet, sondern nur den Namen der Variablen verwendet und sie in einem anderen Kontext bewertet (abgesehen von 'eval'). Auf diese Weise können Sie eine Variable im Kontext "A" verwenden, die nicht im Kontext "B" existiert. Und der Punkt, wenn die unverbindliche Mechanik? Es ist wie der Punkt der Proc, Lambda oder Block-Implementierungen in Ruby. Sie machen das gleiche, nur mit kleinen Unterschieden. UnboundMethod ist nur eines der vielen Tools mit seinen eigenen Eigenschaften (und seinen Unzulänglichkeiten). – karatedog

+0

Es besteht keine intrinsische Notwendigkeit, dass Closures Instanzvariablen enthalten. Blöcke und Procs sind sehr unterschiedliche Dinge. Der eine ist eine Syntax, der andere ist ein tatsächliches Objekt. Wie für normale Procs vs Lambdas - ich benutze beide oft. Dies bedeutet, dass, selbst wenn Unterschiede subtil sind, sie signifikant genug sind, so dass jedes Konstrukt seine eigene Nische hat. Sie sind richtig, dass Ruby mit vielen Mechaniken ausgestattet ist, die eine Person in seiner Produktions-Codebasis so gut wie nie verwenden wird. Aber selbst mit Dingen wie Werfen/Fangen, Fasern und globalen Variablen kann ich zumindest sehen, woher sie kommen. – ndn

2

Ich fasse die guten Anwendungen zusammen, die ich bis jetzt gefunden habe. Weder verwendet unbind obwohl.


Erstens, ein Verfahren zu überschreiben, die alte Implementierung verwenden.Source, dank @WandMaker.

Lassen Sie sagen, Sie so etwas wie dies tun wollen:

class Foo 
    alias old_bar bar 

    def bar 
    old_bar 
    some_additional_processing 
    end 
end 

Dies funktioniert, wird aber hinter sich lassen old_bar, was nicht etwas wünschenswert ist. Stattdessen könnte man tun:

class Foo 
    old_bar = instance_method(:bar) 

    define_method(:bar) do 
    old_bar.bind(self).call 
    some_additional_processing 
    end 
end 

Zweitens eine andere Implementierung einer Methode aus der Hierarchie aufruft.Source.

Das sehr praktische Beispiel in der Post war, dass Sie oft während des Debuggens herausfinden möchten, wo eine Methode definiert wurde. Generell kann man durch das tun:

method(:foo).source_location 

Dies wird jedoch nicht funktionieren, wenn die aktuelle Instanz eine method Methode implementiert, wie es der Fall mit ActionDispatch::Request. In diesem Fall können Sie tun:

Kernel.instance_method(:method).bind(self).call(:foo).source_location 
Verwandte Themen