2

这是一个迷惑你的面条的问题。

class_eval在使用类作为接收器进行评估的块中,方法声明和常量声明之间似乎存在差异。这是一些演示差异的代码:

module X
  def save_block(&block)
    @block = block
  end
end

module Y
  extend X
  save_block do
    SOME_CONSTANT = 1
    def meth
      "a method"
    end
  end
  def self.included(m)
    m.class_eval &@block
  end
end

class Z
  include Y
end

class W
  include Y
end

至少在 Ruby 1.9.3 中执行此代码会导致以下错误:

warning: already initialized constant SOME_CONSTANT

这是我没想到的。首先考虑meth位于哪里:

Z.instance_methods(false)
=> [:meth]

W.instance_methods(false)
=> [:meth]

并且Y没有任何实例方法:

Y.instance_methods(false)
=> []

这是有道理的,因为该块是使用ZW作为class_eval. 但是,常量是不同的,因此会出现错误消息,如下所示:

Y.constants(false)
=> [:SOME_CONSTANT]

Z.constants(false) 
=> []

W.constants(false)
=> []

在这里,常量最终被定义在Y(出于某种奇怪的原因),因此当第二次执行块时,常量SOME_CONSTANT已经定义了。

我非常期待一些启示。

更新(2012/11/19):

正如@phoet 在下面指出的(对我对他的回答的评论的回应),常量是在块的词法范围内定义的,也就是说,在最初定义块的范围内。在我的示例中,这将是 Y。

4

1 回答 1

1

如本问题所述,访问常量常量处理在其“词法范围”内,即定义它们的上下文,而不是执行它们的上下文。

于 2012-11-20T20:00:53.170 回答