Ruby发送vs __send__

时间:2011-01-11 13:48:02

标签: ruby syntax

我理解some_instance.send的概念,但我想弄清楚为什么你可以两种方式调用它。 Ruby Koans暗示除了提供许多不同的方法来做同样的事情之外还有一些原因。以下是两个使用示例:

class Foo
  def bar?
    true
  end
end

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

有人对此有任何想法吗?

5 个答案:

答案 0 :(得分:227)

某些类(例如标准库的套接字类)定义了自己的send方法,该方法与Object#send无关。因此,如果您想使用任何类的对象,您需要使用__send__来保证安全。

现在留下问题,为什么有send而不只是__send__。如果只有__send__,则其他类可以使用名称send而不会产生任何混淆。这样做的原因是send首先存在,之后才意识到名称send也可能在其他情境中有用,所以添加了__send__(这与顺便提一下idobject_id

答案 1 :(得分: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有用的一些情况是该名称是合适的,如消息传递,套接字类等。

答案 2 :(得分:9)

__send__存在,因此不会被意外覆盖。

至于send存在的原因:我不能代表任何其他人,但object.send(:method_name, *parameters)看起来比object.__send__(:method_name, *parameters)更好,所以我使用send除非我需要才能使用__send__

答案 3 :(得分: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方法已公开,因此上述示例将不再有效。

答案 4 :(得分:1)

send __send__和public_send之间的主要区别如下。

  1. send和__send__在技术上与调用Object的方法相同,但是主要区别在于您可以覆盖send方法而不会发出任何警告,并且当您覆盖__send__时会出现警告消息< / li>
  

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

这是因为要避免冲突(尤其是在gem或库中,当使用上下文未知时),请始终使用__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。