我理解some_instance.send
的概念,但我想弄清楚为什么你可以两种方式调用它。 Ruby Koans暗示除了提供许多不同的方法来做同样的事情之外还有一些原因。以下是两个使用示例:
class Foo
def bar?
true
end
end
foo = Foo.new
foo.send(:bar?)
foo.__send__(:bar?)
有人对此有任何想法吗?
答案 0 :(得分:227)
某些类(例如标准库的套接字类)定义了自己的send
方法,该方法与Object#send
无关。因此,如果您想使用任何类的对象,您需要使用__send__
来保证安全。
现在留下问题,为什么有send
而不只是__send__
。如果只有__send__
,则其他类可以使用名称send
而不会产生任何混淆。这样做的原因是send
首先存在,之后才意识到名称send
也可能在其他情境中有用,所以添加了__send__
(这与顺便提一下id
和object_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#include
和Module#extend
方法已公开,因此上述示例将不再有效。
答案 4 :(得分:1)
send __send__
和public_send之间的主要区别如下。
__send__
在技术上与调用Object的方法相同,但是主要区别在于您可以覆盖send方法而不会发出任何警告,并且当您覆盖__send__
时会出现警告消息< / li>
警告:重新定义
__send__
可能会导致严重的问题
这是因为要避免冲突(尤其是在gem或库中,当使用上下文未知时),请始终使用__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。