5

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

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

这里,在第一次调用 methodone时,Ruby 执行外部one方法,但在后续调用中,它只执行内部one方法,完全绕过外部one方法。

我想知道它是如何发生的以及为什么会这样。

4

3 回答 3

7

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

于 2012-07-08T16:19:29.670 回答
7

这个怎么运作

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

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

> my_method; puts; my_method
Old Method
8998420

New Method
8998360

从不同的对象 ID 可以看出,即使这些方法具有相同的名称并附加到同一个对象(通常是控制台上的main),它们实际上是不同的方法对象。但是,由于这些方法是用相同的名称定义的,因此在实例进行方法查找时只能找到最新的定义。

于 2012-07-08T16:30:51.660 回答
3

首先要认识到Ruby 中没有内部或外部方法之类的东西,这一点很重要。

您在方法中定义了一个新方法——在这种情况下,由于正在定义的方法与现有方法具有相同的名称,因此新定义完全覆盖了原始方法。

你所拥有的相当于(也许)更明显的:

class Example
  def one
    self.class.send(:define_method, :one) do
      @value = 99
    end
    puts "Expensive Call"
    @value = 99 # assume its expensive call
  end
end

这里更清楚的是,您正在类的上下文中定义一个方法。

于 2012-07-08T16:33:15.573 回答