【问题标题】:Why direct assignment of constant within Class.new code block does not work为什么在 Class.new 代码块中直接分配常量不起作用
【发布时间】:2018-09-27 04:00:07
【问题描述】:

考虑以下代码:

c1 = Class.new { ANSWER = 42 }
#⇒ #<Class:0x00556c8fc09c60> < Object
c1.constants
#⇒ []
c1.new.class.constants
#⇒ []
c1.new.singleton_class.constants
#⇒ []
Object.constants.grep /ANS/
#⇒ [:ANSWER]

该常量似乎是在Object 上定义的。但是通过对const_set 的显式调用,常量赋值可以完美地工作:

c2 = Class.new { const_set :ANSWER, 42 }
#⇒ #<Class:0x00556c8f7f3568> < Object
c2.constants
#⇒ [:ANSWER]
c2.new.class.constants
#⇒ [:ANSWER]
c2.new.singleton_class.constants
#⇒ [:ANSWER]

我的问题是:是什么阻止了在第一个 sn-p 中将常量正确分配给 Class.new 实例

【问题讨论】:

  • @MarcinKołodziej 似乎last answer there 阐明了他的问题,并且词汇范围在这里很重要。我们似乎需要一个明确的class 关键字来打开新范围。
  • 这确实很疯狂,但认为理所当然的define_method 与明确的def 的工作方式相似,也许这是故意的。
  • Class.new { self::ANSWER = 42 } 可以解决问题,如果您想更明确一点,请使用Class.new { |c| c::ANSWER = 42 }

标签: ruby constants


【解决方案1】:

There are three implicit contexts in Ruby:

  • self(无接收消息发送的上下文和实例变量)
  • 默认定义(没有明确目标的def 方法定义表达式的上下文,即def bar 而不是def foo.bar
  • 默认常量定义点

不幸的是,虽然我上面链接的文章列出了所有这三个,但它只讨论了前两个并将默认常量定义点推迟到以后的文章中,该文章从未写过。

无论如何,记住这三种情况很重要,并注意它们什么时候改变,什么时候不改变。

特别是,一个块改变词法上下文没有别的。一个块不会改变self,它不会改变默认定义,也不会改变默认常量定义点

但是,有些方法的显式目的是改变这三个上下文中的一个或多个。

*_eval 系列方法改变了self(上下文#1)和默认的definee(上下文#2),但它确实改变了默认的常量定义点(上下文#3 )。特别是,所有*_eval (*_exec) 方法都将self 设置为接收者。 instance_* 版本将默认定义者设置为接收者的单例类,module_*class_* 版本将默认定义者设置为接收者。

然而,默认常量定义点没有改变,所以常量定义(和查找)和以前一样工作:定义转到最近的词法封闭模块定义,查找从最近的词法封闭模块定义并按词法向外和动态向上-按继​​承进行。

据我所知,更改默认常量定义点的唯一构造module/class 定义。

【讨论】:

  • 谢谢。这个答案像往常一样出色。
【解决方案2】:

Class::new 的文档指出,“如果给定一个块,则将其传递给类对象,并在此类的上下文中评估该块,例如 class_eval。”,这意味着

c = Class.new { ANSWER = 42 }

等价于

c = Class.new
c.class_eval  { ANSWER = 42 }

class_eval 在顶层创建常量,因为它是在顶层的 c 上调用的。 c.class_eval { ANSWER = 42 }其实是一样的

ANSWER = 42

Object 上创建一个常量。

Object::ANSWER
  #=> 43

这是相反的方式。

NUMBER = 43
Object::NUMBER
  #=> 43

c = Class.new
c.const_set(:NUMBER, 48)
c::NUMBER
  #=> 48
c.class_eval { puts NUMBER }
43

这是另一个例子。

class F
  class G; end
  G.class_eval { HIPPO = 5 }
end

F::HIPPO
  #=> 5
F::G::HIPPO
  #=> #NameError (uninitialized constant F::G::HIPPO)
  #   Did you mean?  F::HIPPO

然而,

class F
  class G
    class_eval { HIPPO = 5 }
  end
end

F::HIPPO
  #=> NameError (uninitialized constant F::HIPPO)
F::G::HIPPO
  #=> 5

读者:请参阅Module#class_evalthis discussion 关于使用class_eval(创建常量的补充)的常量查找。 (搜索"class_eval")。

【讨论】:

  • “并且该块在此类的上下文中进行评估”——这正是我希望ANSWER 定义在c 上而不是@987654340 上的原因@.
猜你喜欢
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
  • 1970-01-01
相关资源
最近更新 更多