【问题标题】:Ruby: define_method vs. defRuby:define_method 与 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...

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-04
        • 1970-01-01
        • 1970-01-01
        • 2017-03-27
        • 1970-01-01
        • 2018-07-26
        • 2017-11-04
        • 2011-05-23
        相关资源
        最近更新 更多