Ruby:define_method 与 def

Posted

技术标签:

【中文标题】Ruby:define_method 与 def【英文标题】:Ruby: define_method vs. def 【发布时间】:2010-09-16 04:52:18 【问题描述】:

作为一个编程练习,我编写了一个 Ruby sn-p,它创建一个类,从该类实例化两个对象,对一个对象进行猴子补丁,并依靠 method_missing 对另一个对象进行猴子补丁。

这是交易。这按预期工作:

class Monkey

  def chatter
    puts "I am a chattering monkey!"
  end

  def method_missing(m)
    puts "No #m, so I'll make one..."
    def screech
      puts "This is the new screech."
    end
  end
end

m1 = Monkey.new
m2 = Monkey.new

m1.chatter
m2.chatter

def m1.screech
  puts "Aaaaaargh!"
end

m1.screech
m2.screech
m2.screech
m1.screech
m2.screech

您会注意到我有一个method_missing 参数。我这样做是因为我希望使用 define_method 来动态创建具有适当名称的缺失方法。但是,它不起作用。事实上,即使使用带有静态名称的 define_method,如下所示:

def method_missing(m)
  puts "No #m, so I'll make one..."
  define_method(:screech) do
    puts "This is the new screech."
  end
end

以以下结果结束:

ArgumentError: wrong number of arguments (2 for 1)

method method_missing   in untitled document at line 9
method method_missing   in untitled document at line 9
at top level    in untitled document at line 26
Program exited.

使错误消息更令人困惑的是我只有一个参数method_missing...

【问题讨论】:

【参考方案1】:
def method_missing(m)
    self.class.class_exec do
       define_method(:screech) puts "This is the new screech."
    end 
end

screech 方法将适用于所有 Monkey 对象。

【讨论】:

【参考方案2】:

define_method 是对象 Class 的(私有)方法。您是从一个实例调用它。没有名为define_method 的实例方法,因此它递归到您的method_missing,这次使用:define_method(缺少的方法的名称)和:screech(您传递给define_method 的唯一参数)。

试试这个(在所有 Monkey 对象上定义新方法):

def method_missing(m)
    puts "No #m, so I'll make one..."
    self.class.send(:define_method, :screech) do
      puts "This is the new screech."
    end
end

或者这个(只在被调用的对象上定义它,使用对象的“特征类”):

def method_missing(m)
    puts "No #m, so I'll make one..."
    class << self
      define_method(:screech) do
        puts "This is the new screech."
      end
    end
end

【讨论】:

这是一个很好的答案,Avdi,它解决了我的其他一些问题。谢谢。 作为一般规则,这说明了为什么您应该始终 a) 在 method_missing 中使用白名单,以便您处理您实际使用的那些方法 感兴趣并且 b) 使用super 转发您不想想要处理“食物链上游”的所有事情。 @JörgWMittag 我可以多说几句 b) 使用 super 转发所有你不想处理“食物链上游”的东西。 ?【参考方案3】:

self.class.define_method(:screech) 不起作用,因为 define_method 是私有方法 你可以这样做

class << self
    public :define_method
end
def method_missing(m)
puts "No #m, so I'll make one..."
Monkey.define_method(:screech) do
  puts "This is the new screech."
end

【讨论】:

以上是关于Ruby:define_method 与 def的主要内容,如果未能解决你的问题,请参考以下文章

ruby define_methodメソッドでコードをシンプルに书く

带参数的 Ruby DSL 定义方法

在 Ruby 中动态定义命名类

你如何将参数传递给define_method?

在define_method定义的方法上调用super

使用defineu方法定义实例和类方法