使用“发送”而不是普通的方法调用有啥意义?

Posted

技术标签:

【中文标题】使用“发送”而不是普通的方法调用有啥意义?【英文标题】:What is the point of using "send" instead of a normal method call?使用“发送”而不是普通的方法调用有什么意义? 【发布时间】:2013-01-25 10:15:53 【问题描述】:

据我了解'发送'方法,这个

some_object.some_method("im an argument")

和这个一样

some_object.send :some_method, "im an argument"

那么使用 'send' 方法有什么意义呢?

【问题讨论】:

因为send的参数是符号,可以保存在变量中。例如,foo = :size; [].send foo。虽然它可用于访问私有方法,但 IMO 将其用作通用消息传递工具的价值远远超过其作为“绕过”开发人员意图的方式的价值。 【参考方案1】:

如果您事先不知道方法的名称,它会派上用场,例如在进行元编程时,您可以将方法的名称放在变量中并将其传递给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,但只能调用公共方法。

【讨论】:

第二个用例比第一个用例 IMO 重要。 如果您的唯一意图是动态调用,您可以使用public_send 使用发送并不是一个坏习惯。 @christianblais 他说使用 send 调用私有方法是一种不好的做法,他是对的,因为做任何事情来调用私有方法都是一种不好的做法(他们'出于某种原因重新私有化)。 改写并切换了用例,感谢您指出这一点。【参考方案2】:

除了 Intrepidd 的用例之外,当您想在同一个接收器和/或参数上路由不同的方法时,它会很方便。如果你有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
)

或者通过使用哈希,甚至这样:

NextMethod = blah_blah: :do_this, whatever: :do_that, ...
some_object.send(NextMethod[:foo], *some_arguments)

【讨论】:

【参考方案3】:

除了其他人的答案之外,一个很好的用例是迭代包含递增数字的方法。

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 代码和模板干燥。这是一个非常具体的案例,但我认为这是一个有效的案例。

【讨论】:

我会说任何依赖于递增数字的命名都是严重的代码气味。上面的类可能想要一个像def attributes; ["foo", "bar", "baz"]; end 这样的方法。 你是对的。那时我是个白痴。但很明显,send 作为元编程工具仍然占有一席之地。 明确一点:我从来没有称你为白痴,我也不认为你是白痴。您的send 示例是完全有效的,但希望额外的见解对经验不足的未来读者有用。【参考方案4】:

简单总结一下同事已经说过的话: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" 

【讨论】:

【参考方案5】:

我喜欢这个结构

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

【讨论】:

以上是关于使用“发送”而不是普通的方法调用有啥意义?的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中定义为static变量的时候,该变量有啥意义吗?

通过普通函数而不是 jQuery 对象方法调用打开 Bootstrap 模式

java中类的构造方法和普通的方法在使用上有啥区别?

Python 中的 classmethod 和 staticmethod 有啥具体用途

在实现的类中调用接口方法有啥用? [复制]

牛逼哄哄的 RabbitMQ 到底有啥用?