1

我正在编写一个可以动态创建方法的类。但是,当我将此代码(方法)嵌套add_method在递归函数中时,我新创建的方法会采用第二次递归传递的值,而不是保留第一次传递的值。

运行下面的示例代码可能更好地解释我的问题:

class Klass
  def initialize

    in_frame(:id => 'first_frame') do |frame|
      add_method "bar", {:frames => frame}

      in_frame({:id => 'second_frame'}, frame) do |frame|
        #do absolutely nothing
      end

    end
  end

  def add_method(name, identifiers)
    puts "adding the #{name} method with these frames #{identifiers}"

    define_singleton_method(name){
      puts "I shouldn't see a second_frame here:  #{identifiers}"
    }
  end

  def in_frame(identifier, frame=[], &block)
    frame << identifier
    block.call(frame)
  end
end

Foo = Klass.new
Foo.bar

第二帧由“second_frame”标识,但该方法是在 first_frame 块中创建的,并且该方法在第二个递归代码块开始之前完全创建并完成。

那么如果是这样的话,为什么 Foo.bar 会返回 second_frame?

4

1 回答 1

1

解决方案

您需要先创建对象的副本,frame然后再在in_frame.

def in_frame(identifier, frame=[], &block)
  frame = frame.dup << identifier
  block.call(frame)
end

解释

当您遵循对象身份时,您观察到的行为是完全有效的。所以让我们跟随frame它的旅程:)

1 def initialize
2   in_frame(:id => 'first_frame') do |frame|
3     add_method "bar", {:frames => frame}
4     in_frame({:id => 'second_frame'}, frame) do |frame|
5       #do absolutely nothing
6     end
7   end
8 end

在第 1 行中,我们in_frame使用参数:id => 'first_frame'和隐式调用frame=[]。这里是作为附加frame的新数组诞生的。:id => 'first_frame'

在第 3 行的开头,我们frame看起来像[:id => 'first_frame']- 它仍然是我们在第 1 行中创建的同一个对象。我们执行add_method,将我们的宝贝frame作为参数传递。在add_method我们里面我们做一个puts打印frame当前状态的。

用这些帧添加 bar 方法 {:frames=>[{:id=>"first_frame"}]}

此外,我们定义了一个方法bar,它持有对 的引用frame但还没有对它做任何事情。

在第 4 行,我们调用in_frame一个空块。in_frame将我们的frame(仍然是同一个对象)作为参数并附:id => 'second_frame'加到它。现在我们的框架看起来像[{:id=>"first_frame"}, {:id=>"second_frame"}]

当我们Foo.bar稍后调用时,我们frame再次打印我们的(它仍然与第 1 行中的对象相同),因为它在 method 的绑定中保存为引用bar

我不应该在这里看到 second_frame:{:frames=>[{:id=>"first_frame"}, {:id=>"second_frame"}]}

在整个过程中,我们操作同一个frame对象:打印它,将东西附加到它,然后再次打印它。

于 2013-08-01T06:30:56.507 回答