在 Ruby 中在运行时从模板定义方法

3

假设我有以下几个类:

class Foo
  attr_accessor :name, :age
end

class Bar
    def initialize(name)
        @foo = Foo.new
        @foo.name = name
    end
end

我想在 Bar 上定义一个访问器,它只是 foo.name 的别名。非常简单:

def name
  @foo.name
end

def name=(value)
  @foo.name = value
end

如果只有一个属性,那么这很容易。但是,假设Foo公开了我想要通过Bar公开的多个属性。我不想手动定义每个方法,而是想像这样做,尽管我知道语法不正确:

[:name, :age].each do |method|
  def method
    @foo.method
  end

  def method=(value)
    @foo.method = value
  end
end

那么...定义这样的方法的正确方式是什么?



1
顺便提一下,你的示例代码实际上不会起作用,因为 foo 是一个局部变量,并且在 initialize 方法结束时超出了范围。 - sepp2k
3个回答

2

要动态定义一个方法,您可以使用define_method,该方法将方法名称作为符号或字符串参数。

要动态调用一个方法,您可以使用send,它也将方法名称作为符号或字符串。

[:name, :age].each do |method|
  define_method(method) do
    @foo.send(method)
  end

  define_method("#{method}=") do |value|
    @foo.send("#{method}=", value)
  end
end

啊,你比我快了。我喜欢我们的代码几乎一模一样。 :-) - Bob Aman

1
[:name, :age].each do |method|
  define_method(method) do
    foo.send(method)
  end

  define_method("#{method}=") do |value|
    foo.send("#{method}=", value)
  end
end

太好了,正是我想要的。出于好奇,使用foo.send是否存在性能问题?我希望将方法的主体定义为字符串,并将“method”替换为操作名称。 - Paul Alexander
是的,Object#send 比较慢。这是否重要完全取决于您以这种方式调度方法的频率。要将方法体定义为 String,您需要使用其中一个 eval 方法,这样会更慢。它还可能带来许多安全隐患。 - Bob Aman

1
请参阅 Ruby 标准库中的 Delegator,具体取决于您想要传递多少个方法。

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