跳到主要内容

Ruby 动态调用方法

本章节我们会讲解学习如何在 Ruby 中如何动态调用方法。

1. 如何动态调用方法

我们使用send来动态调用方法。

实例:

class Cloud 

def rain
p "rain"
end

def hurricane
p "hurricane"
end

end

cloud = Cloud.new
cloud.send(:rain)
cloud.send(:hurricane)
p "====================="
cloud.rain
cloud.hurricane


# ---- 输出结果 ----
"rain"
"hurricane"
"###################"
"rain"
"hurricane"

解释:

在此示例中,我们定义了一个具有两个方法的类,然后使用send方法调用了每个方法。为了进行比较,我们还使用点运算符(.)以通常的方式调用了相同的方法。

**注意事项:**方法名的类型通常使用Symbol,字符串也没有问题。

cloud = Cloud.new
cloud.send(:rain)
cloud.send(:hurricane)
p "====================="
cloud.send("rain")
cloud.send("hurricane")

"rain"
"hurricane"
"rain"
"hurricane"

2. 动态调用方法传递参数

2.1 传递往常一样的参数

要使用send将参数发送给方法,我们需要在方法名称后指定参数。我们可以放多少东西没有限制。

实例:

class ParametersTest

def method\_with\_positional\_parameter(a)
p a
end

def method\_with\_few\_positional\_parameters(a,b,c)
p a,b,c
end

def method\_with\_infinity\_args(\*a)
p a
end

def method\_with\_keyword\_parameter(keyword_parameter:)
p keyword_parameter
end

end

test = ParametersTest.new
test.send(:method\_with\_positional\_parameter, "Hello!")
test.send(:method\_with\_few\_positional\_parameters, "Hi!", "Hello!", "Hola!")
test.send(:method\_with\_infinity\_args, "first", "second", "third")
test.send(:method\_with\_keyword\_parameter, keyword_parameter: "keywooord parameter")

# ---- 输出结果 ----
"Hello!"
"Hi!"
"Hello!"
"Hola!"
["first", "second", "third"]
"keywooord parameter"

我们可以看到,传递块没有什么区别,在两种情况下,我们收到的结果都是相同的。

2.2 传递一个block

实例:

class Cloud 

def rain
p "rain"
yield
end

def hurricane
p "hurricane"
yield
end

end

cloud = Cloud.new
cloud.send(:rain) { p "It's raining! "}
cloud.send(:hurricane) { p "Wow! I better stay home today!"}
p "###################"
cloud.rain { p "It's raining! "}
cloud.hurricane { p "Wow! I better stay home today!"}


# ---- 输出结果 ----
"rain"
"It's raining! "
"hurricane"
"Wow! I better stay home today!"
"###################"
"rain"
"It's raining! "
"hurricane"
"Wow! I better stay home today!"

传递参数的方式非常明显,方法调用的结构变化不大。

注意事项:

如果方法名称指定不正确,我们将看到一个异常。

实例:

class ExampleClass

def method_name
end

end

instance = ExampleClass.new
instance.send(:method\_method\_name)

# ---- 输出结果 ----
NoMethodError: undefined method `method_method_name' for #<ExampleClass:0x0000000194ff10>
Did you mean? method_name
from (irb):34
from /usr/bin/irb:11:in `<main>'

3. 动态调用方法的实例

当我们想一次调用多个方法时,最适合使用send方法。

想象一下,我们拥有一些包含许多不同引擎的技术,我们需要启动每个引擎。

实例:

class SomeTechnology

def turn_on
lower_engine
upper_engine
left_upper_engine
right_upper_engine
end

private

def lower_engine
p "lower engine wroom wooom"
end

def upper_engine
p "upper engine wroom wroom"
end

def left_upper_engine
p "left upper engine wroooooom"
end

def right_upper_engine
p "right upper engine wroooooom"
end

end

我们可以注意到,首先是所有方法名称中都有一个通用词“ engine”。第二个问题是,如果方法数量增加,我们将不得不扩展turn_on方法。

class SomeTechnology

ENGINES = [:lower, :upper, :left\_upper, :right\_upper]

def turn_on
ENGINES.each do |name|
send("#{name}\_engine")
end
end

private

def lower_engine
p "lower engine wroom wooom"
end

def upper_engine
p "upper engine wroom wroom"
end

def left_upper_engine
p "left upper engine wroooooom"
end

def right_upper_engine
p "right upper engine wroooooom"
end

end

tech = SomeTechnology.new
tech.turn\_on

# ---- 输出结果 ---
"lower engine wroom wooom"
"upper engine wroom wroom"
"left upper engine wroooooom"
"right upper engine wroooooom"

这样我们代码的扩展性就变得高了。

4. 小结

本章中我们学习了:

  • send方法在Object类中定义作为参数,我们可以传递字符串或字符。
  • 我们可以传递一个块,就像通常的方法调用一样。
  • 我们也可以像往常一样传递所有相同的参数。
  • 如果方法名称指定不正确,我们将看到一个异常。
  • 当我们动态获取方法名称时,此方法非常理想。
  • 当我们想一次调用多个方法时,send方法是合适的。