Ruby发送vs __发送__


151

我了解的概念,some_instance.send但我想弄清楚为什么您可以同时使用这两种方法。Ruby Koans暗示,除了提供许多不同的方法来执行相同操作之外,还有其他原因。这是两个用法示例:

class Foo
  def bar?
    true
  end
end

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

有人对此有任何想法吗?

Answers:


242

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

现在send剩下的问题是,为什么存在而不是仅仅存在__send__。如果只有__send__名称send,则其他类可以使用该名称,而不会造成任何混淆。其原因在于,send存在第一,只是后来人们意识到,名称send也可能有益地在其他情况下使用,所以__send__加入(这与发生同样的事情idobject_id方式)。


8
此外,BasicObject(在Ruby 1.9中引入)仅具有__send__,而没有send
2014年

好答案。如果提到它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__在同一方法的两个别名,你可能会在第三感兴趣,somwhat不同的可能性,这是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__而不是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。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.