Ruby - define_method和closures

Nor*_*wap 3 ruby reflection closures self

define_method表现出以下行为:

class TestClass
  def exec_block(&block) ; yield ; end
end
TestClass.new.send(:exec_block) do ; puts self ; end
# -> main
TestClass.send(:define_method, :bing) do ; puts self ; end
TestClass.new.bing
# -> <TestClass:...>
Run Code Online (Sandbox Code Playgroud)

我不明白的是传递给define_method的块应该是一个闭包.因此,它应该(至少根据我的理解)捕获在调用时所显示的selfas 的值.mainexec_block

我知道块将成为方法的主体,但我不明白行为的原因.当使用不同的方法时,为什么块会评估不同的东西?

如何使用define_method其他方法重现块的行为?即我怎么能写exec_block它输出<TestClass:...>而不是'main'?

Nik*_* B. 5

self像任何其他变量一样被闭包捕获.我们可以通过传递Proc不同的对象实例来验证:

class A
  def exec_block(&block)
    block.call
  end
end

class B
  def exec_indirect(&block)
    A.new.exec_block(&block)
  end
end

block = proc { p self }
a = A.new; b = B.new

a.exec_block(&block)    # => main
b.exec_indirect(&block) # => main
Run Code Online (Sandbox Code Playgroud)

但是,同样动态地BasicObject#instance_eval重新绑定self变量:

为了设置上下文,在代码执行时将变量self设置为obj,使代码可以访问obj的实例变量

Module#define_method反过来用于instance_eval执行相关的块:

如果指定了块,则将其用作方法体.使用instance_eval [...]评估此块

注意:

A.send(:define_method, :foo, &block)
a.foo                   # => #<A:0x00000001717040>
a.instance_eval(&block) # => #<A:0x00000001717040>
Run Code Online (Sandbox Code Playgroud)

有了这些知识,您现在可以重写exec_block使用instance_eval:

class A
  def exec_block(&block)
    instance_eval(&block)
  end
end

block = proc { p self }
A.new.exec_block(&block)  # => #<A:0x00000001bb9828>
Run Code Online (Sandbox Code Playgroud)

如前所述,使用instance_eval似乎是运行Proc具有已修改上下文的实例的唯一方法.它可以用于在Ruby中实现动态绑定.