Ruby中的send()方法是什么?

147

有人能告诉我以下代码片段的含义吗?

obj.send("#{method_name}")

is和does是什么意思?


2
这是整行代码吗?我的意思是,在“send”之前没有其他内容了吗? - giraff
8个回答

162

send方法会向一个对象实例及其在类层次结构中的祖先发送一条消息,直到某个方法响应该消息(因为它的名字与第一个参数匹配)。

实际上,这两行代码是等价的:

1.send '+', 2
1.+(2)
1 + 2
请注意,send 方法可以绕过可见性检查,这意味着您也可以调用私有方法(对单元测试很有用)。
如果在 send 之前确实没有变量,那就意味着全局对象被使用了。
send :to_s    # "main"
send :class   # Object

1
哦,我明白了,那么如果想把类似于1个月这样的内容存储到数据库中而不是静态地说出天数,就可以使用“send”。 - Christian Bankester
3
是的,你可以使用它来调用计算而非静态的方法。(不过,为了避免调用私有方法,你不应该允许无限制的用户输入... 但你可以给它们一个唯一的前缀:发送'user_method_'+方法名,*args) - giraff
2
一个很好的使用案例可能是,如果你想测试一个受保护的类方法,你可以在测试文件中在类外部调用它。 - GN.

135

send 是 Ruby 中的一个方法,可以通过指定方法名和对应参数来调用另一个方法。

 class Klass
   def hello(*args)
     "Hello " + args.join(' ')
   end
 end
 k = Klass.new
 k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"

来源


81

我认为 .send 方法中最有用的功能之一是它可以动态调用方法。这可以节省很多输入。 .send 方法最常见的用法之一是动态分配属性。例如:

class Car
  attr_accessor :make, :model, :year
end  

要定期分配属性,需要

c = Car.new
c.make="Honda"
c.model="CRV"
c.year="2014"
使用.send方法也可以:
c.send("make=", "Honda")
c.send("model=", "CRV")
c.send("year=","2014")

但是所有这些都可以用以下方式替换:

假设您的Rails应用程序需要将用户输入的属性分配给您的car类,您可以执行以下操作

c = Car.new()
params.each do |key, value|
  c.send("#{key}=", value)
end

9
以这种方式使用.send会增加不必要的复杂度,并使向代码中无意中引入错误更加容易。例如,在上述代码中,如果您向参数哈希表中添加一个新条目(如“cylinders”),则代码将因出现未定义方法错误而失败。 - Kevin Schwerdtfeger
3
如果需要的话,可以使用"respond_to?"来防止这样的错误。 - Richard_G
1
@Kevin 你是对的,但有时可能是必要的。更多的灵活性意味着更多的风险,这可以得到缓解。 - Will Sheppard
当我使用send时,我总是会rescue NoMethodError,并将错误发送到日志和/或实现一些默认行为,以应对未定义方法的情况。 - Matthew

18

另一个类似于Antonio Jha的例子:https://dev59.com/oXA75IYBdhLWcg3wW3u8#26193804

如果你需要读取对象的属性,则可以使用类似的方法。

例如,如果你有一个字符串数组,如果尝试通过迭代并调用它们的对象来处理它们,那么这将不起作用。

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.a
end
# => NoMethodError: undefined method `a'

然而,您可以将字符串send到对象:

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.send(a)
end
# => Vandalay Project
# => A very important project

谢谢!这正是我想要的答案。不知道这个常用吗?我在遗留代码中发现了类似的东西,不确定是否应该坚持使用它。@Mike Vallano - B Liu
1
@b-liu 我曾经看到有经验的开发人员在新代码中使用它。当使用define_method时,它也可能会有所帮助:https://apidock.com/ruby/Module/define_method。 - Mike Vallano

14

send做什么?

send是另一种“调用方法”的方式。例如:

o = Object.new
o.to_s # => "#<Object:0x00005614d7a24fa3>"
# is equivalent to:
o.send(:to_s) # => "#<Object:0x00005614d7a24fa3>"

Send存在于Object类中

这有什么好处?

这种方法的好处是,您可以将要调用的方法作为参数传递。以下是一个简单的示例:

def dynamically_call_a_method(method_name)
    o = Object.new
    o.send method_name
end
dynamically_call_a_method(:to_s) # => "#<Object:0x00005614d7a24fa3>"

您可以传入您想要调用的方法。在这种情况下,我们传入了:to_s。当进行Ruby元编程时,这非常方便,因为它允许我们根据不同的要求调用不同的方法。


5

发送还可以用作展示 Ruby 中所有内容都是对象的方式

1.send(:+, 1)  ## -> 2
3.send(:*, 2)  ## -> 6

2

视图的另一个使用案例:

    <%= link_to 
    send("first_part_of_path_#{some_dynamic_parameters}_end_path", 
    attr1, attr2), ....
    %>

允许您编写可扩展的视图,可与各种对象一起使用:

    render 'your_view_path', object: "my_object"

这会在视图中添加不必要的逻辑,并可能存在安全隐患。不要这样做。请使用数组和哈希表。 - Derrek Bertrand

1

我对这个话题来说有点晚了。作为一个新手,我只是使用它,并希望对像我一样想要直接的答案的人有所帮助。 filter_hash.each{|k,v| order_additional_hash[send(method_name, k)] = v}

如上所示,send(method_name)是我们想要调用的方法。如果方法名作为字符串传递给参数并且k是我们想要传递给方法的参数,则匹配名称并调用该方法。


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