使用“send”而不是普通方法调用的意义是什么?

14

就我理解的“send”方法而言,这个

some_object.some_method("im an argument")

与此相同

some_object.send :some_method, "im an argument"

那么使用“send”方法的重点是什么?


因为send方法的参数是一个符号,可以保存在变量中。例如:foo = :size; [].send foo。虽然它可以用于访问私有方法,但我认为它作为一种通用消息传递工具的价值远远超过了作为绕过开发者意图的方式。 - Dave Newton
5个回答

20

如果您不事先知道方法的名称,当您进行元编程时,可以将方法名称存储在变量中并传递给send方法,这样会非常方便。

它还可用于调用私有方法,尽管大多数Ruby开发人员认为这种特定用法不是很好的实践。

class Test
  private
  def my_private_method
    puts "Yay"
  end
end

t = Test.new

t.my_private_method # Error

t.send :my_private_method #Ok

你可以使用public_send,只能调用公共方法。


8
在我看来,第二个用例比第一个重要得多 - Dave Newton
4
如果您只想进行动态调用,可以使用public_send - Andrew Marshall
使用 send 并不是一种不好的实践。 - christianblais
@christianblais 他说使用 send 调用私有方法是一种不好的做法,他是对的,因为任何调用私有方法的行为都是不好的(它们被声明为私有是有原因的)。 - Michelle Tilley
重新表述并切换了用例,感谢指出。 - Intrepidd

6
除了Intrepidd的用例之外,当您想要在同一接收器和/或参数上路由不同的方法时,send非常方便。如果您有some_object,并且想根据foo的内容对其执行不同的操作,则在没有send的情况下,您需要编写类似以下的代码:
case foo
when blah_blah then some_object.do_this(*some_arguments)
when whatever then some_object.do_that(*some_arguments)
...
end

但是如果你有send,你可以这样写:

next_method =
case foo
when blah_blah then :do_this
when whatever then :do_that
....
end
some_object.send(next_method, *some_arguments)

或者

some_object.send(
  case foo
  when blah_blah then :do_this
  when whatever then :do_that
  ....
  end,
  *some_arguments
)

或者通过使用哈希(hash)来实现,甚至可以做到这一点:
NextMethod = {blah_blah: :do_this, whatever: :do_that, ...}
some_object.send(NextMethod[:foo], *some_arguments)

4
除了其他人的回答之外,一个很好的使用案例是迭代包含递增数字的方法。
class Something
  def attribute_0
    "foo"
  end
  def attribute_1
    "bar"
  end
  def attribute_2
    "baz"
  end
end

thing = Something.new

3.times do |x|
  puts thing.send("attribute_#{x}")
end

#=> foo
# bar
# baz

这看起来可能微不足道,但有时它会帮助我保持我的Rails代码和模板DRY。这是一个非常特定的情况,但我认为它是有效的。


我认为任何依赖于递增数字的命名都是严重的代码异味。上述类可能需要一个像 def attributes; ["foo", "bar", "baz"]; end 这样的方法。 - Jezen Thomas
你说得对。我以前真是个白痴。但是显然,send作为元编程工具仍然有其存在的价值。 - Ten Bitcomb
1
要明确一点:我从未称呼你为白痴,也不认为你是一个。你提到的“send”的例子完全有效,但希望这些额外的见解对于那些经验不足的读者有所帮助。 - Jezen Thomas

0
简单总结一下同事们已经说过的:`send`方法是元编程的语法糖。下面的示例演示了当原生调用方法可能不可行时的情况:
class Validator
  def name
    'Mozart'
  end
  def location
    'Salzburg'
  end
end

v = Validator.new
'%name% was born in %location%'.gsub (/%(?<mthd>\w+)%/) do
  # v.send :"#{Regexp.last_match[:mthd]}"
  v.send Regexp.last_match[:mthd].to_sym
end
=> "Mozart was born in Salzburg" 

-1

我喜欢这个结构

Object.get_const("Foo").send(:bar)

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