跳到主要内容

动态定义方法

本章节我会为大家讲一下 Ruby 元编程中如何动态定义方法。

1. 如何动态定义一个方法

我们会使用define_method来动态定义方法。

实例:

class Account
attr_accessor :state

(1..99).each do |i|
define\_method("credit\_#{i}".to_sym) do
self.state = state + i
end

define\_method("debit\_#{i}".to_sym) do
self.state = state - i
end
end

def initialize(state)
@state = state
end
end

account = Account.new(20)
account.credit_31
account.state # => 51
account.debit_45
account.state # => 6

方法会被显示到 public_methods 列表中。

account.public\_methods(false)
# [ ... , :credit\_83, :debit\_83, :credit\_84, :debit\_84, ...]
account.respond_to?(:debit\_19)
# => true
method = account.public\_method(:debit\_19)
# => #<Method: Account#debit\_19>

2. 动态定义方法时传递参数

2.1 强制参数

实例:

class Bar
define\_method(:foo) do |arg1, arg2|
arg1 + arg2
end
end

a = Bar.new
a.foo
#=> ArgumentError (wrong number of arguments (given 0, expected 2))
a.foo 1, 2
# => 3

2.2 可选参数

class Bar
define\_method(:foo) do |arg=nil|
arg
end
end

a = Bar.new
a.foo
#=> nil
a.foo 1
# => 1

2.3 任意数量参数

实例:

class Bar
define\_method(:foo) do |arg=nil|
arg
end
end

a = Bar.new
a.foo
#=> nil
a.foo 1
# => 1

2.4 固定键值对的哈希参数

实例:

class Bar
define\_method(:foo) do |option1: 'default value', option2: nil|
"#{option1} #{option2}"
end
end

bar = Bar.new
bar.foo option2: 'hi'
# => "default value hi"
bar.foo option2: 'hi',option1: 'ahoj'
# => "ahoj hi"

2.5 任意键值对数量的哈希参数

实例:

class Bar
define\_method(:foo) do |\*\*keyword_args|
keyword_args
end
end

bar = Bar.new
bar.foo option1: 'hi', option2: 'ahoj', option3: 'ola'
# => {:option1=>"hi", :option2=>"ahoj", :option3=>"ola"}

2.6 传递block

实例:

class Bar
define\_method(:foo) do |&block|
p block.call
end
end

bar = Bar.new
bar.foo do
'six'
end

# => "six"

2.7 所有参数

实例:

class Bar
define\_method(:foo) do |variable1, variable2, \*arg, \*\*keyword_args, &block|
p variable1
p variable2
p arg
p keyword_args
p block.call
end
end

bar = Bar.new
bar.foo :one, 'two', :three, 4, 5, foo: 'bar', car: 'dar' do
'six'
end

## will print:
:one
"two"
[:three, 4, 5]
{ foo: "bar", car: "dar" }
'six'

注意事项:传递所有参数需要按照普通参数、不指定数目的参数、哈希参数、block的顺序来设置。

3. 小结

本章节中我们学习了使用define_method来动态定义方法,如何传递参数,以及传递参数的顺序应该按照arg、*args、**keyword_args、block的顺序来设置。