Ruby send vs __send__

183

我理解some_instance.send的概念,但我试图弄清楚为什么可以这两种方式调用它。 Ruby Koans暗示除了提供许多不同的方法来执行相同的操作之外,还有一些原因。下面是两个使用示例:

class Foo
  def bar?
    true
  end
end

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

有人对此有任何想法吗?

5个回答

277

有些类(例如标准库的socket类)定义了自己的send方法,与Object#send无关。因此,如果您想使用任何类的对象进行操作,最好使用__send__

现在问题来了,为什么会有send而不只是__send__?如果只有__send__,那么其他类可以使用名称send而不会造成混淆。原因在于,send先存在,只有后来才意识到名称send在其他上下文中也可能有用,因此添加了__send__(顺便说一下,这也是idobject_id发生的情况)。


11
另外,Ruby 1.9 中引入的 BasicObject 只有 __send__ 方法,没有 send 方法。 - Andrew Marshall
好答案。如果提到了public_send可能会更好,因为通常情况下使用它比使用send更好。 - Marc-André Lafortune

37
如果您真的需要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 会发出一个警告:

warning: redefining `__send__' may cause serious problems

一些情况下,重写 send 方法会非常有用,比如在消息传递、套接字类等场景中使用这个名称是合适的。


12

__send__ 存在的原因是为了防止意外覆盖。

至于为什么会有 send:我不知道别人怎么看,但是 object.send(:method_name, *parameters)object.__send__(:method_name, *parameters) 更简洁易读,所以除非需要使用 __send__,否则我都使用 send


6
除了其他人已经告诉你的内容,也就是说send__send__是同一个方法的两个别名,你可能会对第三种略有不同的可能性感兴趣,即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__技术上用于调用对象的方法,但主要区别在于您可以覆盖send方法而不会收到任何警告,当您覆盖__send__时会出现警告消息。

警告:重新定义__send__可能会导致严重问题

这是因为为了避免冲突,特别是在gems或libraries中,当将其用于未知的上下文时,总是使用__send__而不是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)

最后尝试使用public_send来避免直接调用私有方法,而不是使用__send__或send。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接