instance_eval的行为不一致

时间:2012-07-09 16:29:02

标签: ruby

将块传递给instance_eval时,它意味着在该实例的上下文中执行。 self ,当在该块中显式或隐式引用时,应该引用已调用instance_eval的实例。这似乎在所有情况下都能正常工作,除非传递已转换为proc的方法对象。在这种情况下, self 指的是定义方法的实例,而不是评估块的实例。这是一个代码示例,用于演示我的意思:

class A
  def test(&b)
    instance_eval(&b)
  end
end

class B
  def test_a(a)
    a.test { puts self }
  end

  def test_b_helper(*args)
    puts self
  end

  def test_b(a)
    m = method(:test_b_helper).to_proc
    a.test(&m)
  end
end

a = A.new
b = B.new

b.test_a(a) #<A:0x007ff66b086c68>
b.test_b(a) #<B:0x007fa3e1886bc0>

预期的行为是两个测试都返回相同的输出。在这种情况下, self 应该引用A的实例,而不是B.

我查看了文档并进行了一些搜索,但我无法找到有关这种特性的信息。我希望有一些经验丰富的Rubyist可以帮助消除这种行为上的差异。

只是为了澄清,我使用的是Ruby 1.9.2。

1 个答案:

答案 0 :(得分:3)

区别在于,Blocks和Procs是闭包,而Method对象则不是。

摘录自D. Flanagan,Y。Matsumoto。 Ruby Programming Language ,O'Reilly 2008,p。 204:

  

Method个对象和Proc个对象之间的一个重要区别是   Method对象不是闭包。 Ruby的方法旨在   是完全独立的,他们永远无法访问本地   变量超出了自己的范围。唯一保留的绑定   因此,Method对象是self的值 - 在其上的对象   方法是被调用的。

当您现在转换Method对象to_proc时,将self的值绑定到B的调用实例,因此您将获得上述结果。实际上,在创建self对象时,Method已经修复。

当您考虑以下代码时,这一点尤为明显:

class A
  def foo
    puts 'bar'
  end
end

class B; end

class C < A; end

foo = A.instance_method(:foo)
# => #<UnboundMethod: A#foo>

a = A.new
foo.bind(a).call
# bar

b = B.new
foo.bind(b).call
# TypeError: bind argument must be an instance of A

c = C.new
foo.bind(c).call
# bar

简单地说:self始终固定在MethodUnboundMethod个对象上。