0

当将一个块传递给 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。

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

澄清一下,我使用的是 Ruby 1.9.2。

4

1 回答 1

3

不同之处在于, Blocks 和 Procs 是闭包, Method 对象不是。

摘自 D. Flanagan, Y. Matsumoto。Ruby 编程语言,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对象上。

于 2012-07-09T17:01:23.777 回答