Ruby送信vs __send__


151

私はの概念を理解していsome_instance.sendますが、なぜこれを両方の方法で呼ぶことができるのかを理解しようとしています。Ruby Koansは、同じことを行うためのさまざまな方法を提供する以外にも、いくつかの理由があることを示唆しています。次に2つの使用例を示します。

class Foo
  def bar?
    true
  end
end

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

誰かこれについて何か考えがありますか?

回答:


242

一部のクラス(たとえば、標準ライブラリのソケットクラス)は、sendとは関係のない独自のメソッドを定義しObject#sendます。したがって、任意のクラスのオブジェクトを操作したい場合は、を使用__send__して安全を確保する必要があります。

今そこにある理由葉質問、それsendだけではありません__send__。のみがあった場合は__send__名前がsend混乱することなく、他のクラスで使用することができます。その理由は、つまりsend最初に存在していたとだけ後でそれは名前があることを実現したsendので、また有効他のコンテキストで使用される可能性があります__send__(で起こった同じことだと添加し、idそしてobject_id方法によって)。


8
また、(Ruby 1.9で導入された)BasicObjectにはのみがあり__send__、はありませんsend
アンドリューマーシャル

いい答えだ。それが言及されている場合public_send、それはsendとにかく多くの場合より好まれます。
マルク=アンドレ・Lafortune

31

本当にsend通常のように動作する必要がある場合は、__send__オーバーライドされない(すべきでない)ため、を使用する必要があります。__send__操作されているクラスがどのメソッドを定義しているかわからない場合、使用はメタプログラミングで特に役立ちます。オーバーライドされている可能性がありますsend

見る:

class Foo
  def bar?
    true
  end

  def send(*args)
    false
  end
end

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

をオーバーライドすると__send__、Rubyは警告を発します。

警告: `__send__ 'を再定義すると深刻な問題が発生する可能性があります

オーバーライドsendすることが役立つ場合は、メッセージパッシング、ソケットクラスなど、その名前が適切な場合です。


9

__send__ 誤って上書きできないように存在します。

なぜsend存在するのか:他の人には話せませんがにobject.send(:method_name, *parameters)比べて見栄えが良いのでobject.__send__(:method_name, *parameters)、を使う必要sendない限り使います。__send__


6

他の人がすでにあなたに言ったことsend__send__、結局のところ、それが同じメソッドの2つのエイリアスであることを除けば、3番目のまったく異なる可能性であるに興味があるかもしれませんpublic_send。例:

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

更新:Ruby 2.1以降Module#includeModule#extendメソッドは公開されるため、上記の例は機能しなくなります。


0

send __send__、、およびpublic_send の主な違いは次のとおりです。

  1. sendおよび技術的に__send__はObjectのメソッドの呼び出しに使用されるものと同じですが、主な違いは、警告なしでsendメソッドをオーバーライドできることと、オーバーライド__send__すると警告メッセージが表示されることです。

警告:再定義__send__すると重大な問題が発生する可能性があります

これは、競合を回避するため、特にGemまたはライブラリで、使用されるコンテキストが不明な場合__send__に、送信ではなく常に使用するためです。

  1. send(または__send__)とpublic_sendの違いは、send / __send__はオブジェクトのプライベートメソッドを呼び出すことができ、public_sendは呼び出せないことです。
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)

最後に、__ send__またはsendを使用する代わりにpublic_sendを使用してプライベートメソッドの直接呼び出しを回避してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.