Ruby send vs __send__

151

Ich verstehe das Konzept von, some_instance.sendaber ich versuche herauszufinden, warum Sie dies in beide Richtungen nennen können. Die Ruby Koans implizieren, dass es einen Grund gibt, der nicht nur viele verschiedene Möglichkeiten bietet, dasselbe zu tun. Hier sind die beiden Anwendungsbeispiele:

class Foo
  def bar?
    true
  end
end

foo = Foo.new
foo.send(:bar?)
foo.__send__(:bar?)

Hat jemand eine Idee dazu?

Jaydel
quelle

Antworten:

242

Einige Klassen (zum Beispiel die Socket-Klasse der Standardbibliothek) definieren ihre eigene sendMethode, die nichts damit zu tun hat Object#send. Wenn Sie also mit Objekten einer Klasse arbeiten möchten, müssen Sie __send__auf der sicheren Seite sein.

Nun bleibt die Frage, warum es das gibt sendund nicht nur __send__. Wenn es nur __send__den Namen gäbe, sendkönnte er von anderen Klassen ohne Verwirrung verwendet werden. Der Grund dafür ist, dass sendes zuerst existierte und erst später erkannt wurde, dass der Name sendauch in anderen Kontexten nützlich sein könnte, also __send__wurde er hinzugefügt (das ist das gleiche, was mit idund object_idübrigens passiert ist ).

sepp2k
quelle
8
Außerdem hat BasicObject (eingeführt in Ruby 1.9) nur __send__, nicht send.
Andrew Marshall
Gute Antwort. Könnte sogar noch besser sein, wenn es erwähnt wird public_send, was oft sendsowieso vorzuziehen ist.
Marc-André Lafortune
31

Wenn Sie sich wirklichsend so verhalten müssen , wie es normalerweise der __send__Fall ist , sollten Sie es verwenden , da es nicht überschrieben wird (sollte). Die Verwendung __send__ist besonders nützlich bei der Metaprogrammierung, wenn Sie nicht wissen, welche Methoden die zu manipulierende Klasse definiert. Es hätte überschreiben könnensend .

Beobachten:

class Foo
  def bar?
    true
  end

  def send(*args)
    false
  end
end

foo = Foo.new
foo.send(:bar?)
# => false
foo.__send__(:bar?)
# => true

Wenn Sie überschreiben __send__, gibt Ruby eine Warnung aus:

Warnung: Die Neudefinition von "__send__" kann schwerwiegende Probleme verursachen

In einigen Fällen, in denen ein Überschreiben sinnvoll sendwäre, ist dieser Name angemessen, z. B. Nachrichtenübergabe, Socket-Klassen usw.

Thiago Silveira
quelle
9

__send__ existiert, so dass es nicht versehentlich überschrieben werden kann.

Warum sendgibt es object.send(:method_name, *parameters)das? Ich kann für niemanden sprechen, sieht aber besser aus als object.__send__(:method_name, *parameters), also benutze ich es, es sendsei denn, ich muss es benutzen __send__.

Andrew Grimm
quelle
6

Abgesehen von dem, was andere Ihnen bereits gesagt haben und was darauf hinausläuft, dies zu sagen sendund __send__zwei Aliase derselben Methode sind, könnten Sie an der dritten, etwas anderen Möglichkeit interessiert sein, nämlich public_send. Beispiel:

A, B, C = Module.new, Module.new, Module.new
B.include A #=> error -- private method
B.send :include, A #=> bypasses the method's privacy
C.public_send :include, A #=> does not bypass privacy

Update: Da Ruby 2.1 Module#includeund Module#extendMethoden öffentlich werden, würde das obige Beispiel nicht mehr funktionieren.

Boris Stitnicky
quelle
0

Der Hauptunterschied zwischen send __send__,, und public_send ist folgender.

  1. send und __send__sind technisch identisch mit dem Aufruf der Object-Methode. Der Hauptunterschied besteht jedoch darin, dass Sie die send-Methode ohne Warnung überschreiben können. Wenn Sie sie überschreiben, __send__wird eine Warnmeldung angezeigt

Warnung: Eine Neudefinition __send__kann schwerwiegende Probleme verursachen

Dies liegt daran, dass zur Vermeidung von Konflikten, insbesondere in Edelsteinen oder Bibliotheken, wenn der Kontext, in dem sie verwendet werden, unbekannt ist, immer __send__anstelle von Senden verwendet wird.

  1. Der Unterschied zwischen send (oder __send__) und public_send besteht darin, dass send / __send__die privaten Methoden eines Objekts aufrufen kann und public_send nicht.
class Foo
   def __send__(*args, &block)
       "__send__"
   end
   def send(*args)
     "send"
   end
   def bar
       "bar"
   end
   private
   def private_bar
     "private_bar"
   end
end

Foo.new.bar #=> "bar"
Foo.new.private_bar #=> NoMethodError(private method 'private_bar' called for #Foo)

Foo.new.send(:bar) #=> "send"
Foo.new.__send__(:bar) #=> "__send__"
Foo.new.public_send(:bar) #=> "bar"

Foo.new.send(:private_bar) #=> "send"
Foo.new.__send__(:private_bar) #=> "__send__"
Foo.new.public_send(:private_bar) #=> NoMethodError(private method 'private_bar' called for #Foo)

Versuchen Sie am Ende, public_send zu verwenden, um einen direkten Aufruf der privaten Methode zu vermeiden, anstatt __send__ oder send zu verwenden.

Kishor Vyavahare
quelle