我理解some_instance.send
的概念,但我试图弄清楚为什么可以这两种方式调用它。 Ruby Koans暗示除了提供许多不同的方法来执行相同的操作之外,还有一些原因。下面是两个使用示例:
class Foo
def bar?
true
end
end
foo = Foo.new
foo.send(:bar?)
foo.__send__(:bar?)
有人对此有任何想法吗?
有些类(例如标准库的socket类)定义了自己的send
方法,与Object#send
无关。因此,如果您想使用任何类的对象进行操作,最好使用__send__
。
现在问题来了,为什么会有send
而不只是__send__
?如果只有__send__
,那么其他类可以使用名称send
而不会造成混淆。原因在于,send
先存在,只有后来才意识到名称send
在其他上下文中也可能有用,因此添加了__send__
(顺便说一下,这也是id
和object_id
发生的情况)。
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
方法会非常有用,比如在消息传递、套接字类等场景中使用这个名称是合适的。
__send__
存在的原因是为了防止意外覆盖。
至于为什么会有 send
:我不知道别人怎么看,但是 object.send(:method_name, *parameters)
比 object.__send__(:method_name, *parameters)
更简洁易读,所以除非需要使用 __send__
,否则我都使用 send
。
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
Module#include
和Module#extend
方法变为公共方法,因此上述示例将不再起作用。send,__send__
和public_send之间的主要区别如下。
__send__
技术上用于调用对象的方法,但主要区别在于您可以覆盖send方法而不会收到任何警告,当您覆盖__send__
时会出现警告消息。警告:重新定义
__send__
可能会导致严重问题
这是因为为了避免冲突,特别是在gems或libraries中,当将其用于未知的上下文时,总是使用__send__
而不是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。
__send__
方法,没有send
方法。 - Andrew Marshallpublic_send
可能会更好,因为通常情况下使用它比使用send
更好。 - Marc-André Lafortune