这种动态方法定义的工作原理和原因是什么?

sha*_*esh 5 ruby metaprogramming

以下代码如何工作,更重要的是,它为什么会这样工作?

class Example
  def one
    def one
      @value = 99
    end
    puts "Expensive Call"
    @value = 99 # assume its expensive call
  end
end

ex = Example.new
puts ex.one # => "Expensive Call"; 99
puts ex.one # => 99
Run Code Online (Sandbox Code Playgroud)

这里,在第一次调用方法时one,Ruby执行外部one方法,但是在连续调用时,它只执行内部one方法,one完全绕过外部方法.

我想知道它是如何发生的,为什么会这样.

Lin*_*ios 7

当你第一次执行它时,它会在类中重新定义,然后完成.第二次,该方法one本身被覆盖为just @value = 99,因此没有打印任何内容.


Tod*_*obs 6

这个怎么运作

Ruby允许您在运行时重新定义类,因为classdef实际上是可执行代码.在您的示例中,代码执行以下操作:

  1. 定义一个Example#one方法,该方法将在调用实例方法时(重新)定义Example#one方法.
  2. 出于实际目的,在调用外部实例方法之前,不会执行内部def.(头发分割者可能会合理地争论这个定义,但是这会解释为解析器/解释器的细节,这对于本讨论而言并不重要.)
  3. 您定义名为"ex."的Example实例.
  4. 您在ex上调用实例方法,该方法定义了一个具有相同名称的方法.
  5. 再次调用实例方法时,将使用新方法而不是旧方法.

为什么会这样

基本上,方法的最后一个定义替换了该命名空间中的任何早期定义,但这些方法实际上是新对象.您可以按如下方式查看此操作:

def my_method
  puts 'Old Method'
  puts  self.method(:my_method).object_id
  def my_method
    puts 'New Method'
    puts  self.method(:my_method).object_id
  end  
end
Run Code Online (Sandbox Code Playgroud)

如果在irb或pry会话中运行此命令,则可以看到在运行时重新定义的方法:

> my_method; puts; my_method
Old Method
8998420

New Method
8998360
Run Code Online (Sandbox Code Playgroud)

正如您可以通过不同的对象ID看到的那样,即使这些方法具有相同的名称并且附加到同一个对象(通常是控制台上的主要对象),它们实际上也是不同的方法对象.但是,由于方法是使用相同的名称定义的,因此当实例执行方法查找时,只会找到最新的定义.