Ruby 为什么在Class.new代码块中直接赋值常量不起作用
考虑以下代码:Ruby 为什么在Class.new代码块中直接赋值常量不起作用,ruby,constants,Ruby,Constants,考虑以下代码: c1 = Class.new { ANSWER = 42 } #⇒ #<Class:0x00556c8fc09c60> < Object c1.constants #⇒ [] c1.new.class.constants #⇒ [] c1.new.singleton_class.constants #⇒ [] Object.constants.grep /ANS/ #⇒ [:ANSWER] 我的问题是:是什么阻止了在类上正确分配常量。第一个代码片段中的新实例?
c1 = Class.new { ANSWER = 42 }
#⇒ #<Class:0x00556c8fc09c60> < Object
c1.constants
#⇒ []
c1.new.class.constants
#⇒ []
c1.new.singleton_class.constants
#⇒ []
Object.constants.grep /ANS/
#⇒ [:ANSWER]
我的问题是:是什么阻止了在类上正确分配常量。第一个代码片段中的新实例?文档的状态是,“如果给定了一个块,它将传递给类对象,并且该块将在该类的上下文中进行评估,如Class\u eval
”,这意味着
c = Class.new { ANSWER = 42 }
相当于
c = Class.new
c.class_eval { ANSWER = 42 }
class\u eval
在顶级创建常量,因为它是在顶级的c
上调用的c.class_eval{ANSWER=42}
实际上与
ANSWER = 42
它在对象上创建一个常量
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
读者:请参阅和了解使用class\u eval
(创建常量的补充)进行常量查找。(搜索“class_eval”
)。文档中的状态是,“如果给定了一个块,它将传递给类对象,并且该块将在该类的上下文中进行评估,如class_eval
”,这意味着
c = Class.new { ANSWER = 42 }
相当于
c = Class.new
c.class_eval { ANSWER = 42 }
class\u eval
在顶级创建常量,因为它是在顶级的c
上调用的c.class_eval{ANSWER=42}
实际上与
ANSWER = 42
它在对象上创建一个常量
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
读者:请参阅和了解使用class\u eval
(创建常量的补充)进行常量查找。(搜索“等级评估”
)。:
self
(无接收者消息发送的上下文和实例变量)
- 默认定义对象(没有明确目标的
def
方法定义表达式的上下文,即def bar
而不是def foo.bar
)
- 默认常量定义点
不幸的是,虽然我链接到上面的文章列出了这三个变量,但它只讨论了前两个变量,并将默认常量定义点推迟到了后来的一篇文章中,这篇文章从未撰写过
无论如何,记住这三种情况是很重要的,并且要知道它们何时改变,何时不改变
特别是,一个块只改变词法上下文,而不改变其他内容。块不会更改自身,不会更改默认定义对象,也不会更改默认常量定义点
然而,有些方法的明确目的是改变这三种上下文中的一种或多种
*\u eval
方法系列会更改self
(上下文1)和默认的定义对象(上下文2),但不会更改默认的常量定义点(上下文3)。特别是,所有*\u eval
(*\u exec
)方法都将self
设置到接收器。实例*
版本将默认定义对象设置为接收方的单例类,模块*
和类*
版本将默认定义对象设置为接收方
但是,默认的常量定义点没有更改,因此常量定义(和查找)的工作原理与以前一样:定义转到最近的词汇性封闭模块定义,查找从最近的词汇性封闭模块定义开始,并通过继承以词汇性向外和动态向上进行
据我所知,唯一改变默认常量定义点的构造是模块
/类
定义。:
self
(无接收者消息发送的上下文和实例变量)
- 默认定义对象(没有明确目标的
def
方法定义表达式的上下文,即def bar
而不是def foo.bar
)
- 默认的常量定义点
不幸的是,虽然我链接到上面的文章列出了这三个变量,但它只讨论了前两个变量,并将默认常量定义点推迟到了后来的一篇文章中,这篇文章从未撰写过
无论如何,记住这三种情况是很重要的,并且要知道它们何时改变,何时不改变
特别是,一个块只改变词法上下文,而不改变其他内容。块不会更改自身,不会更改默认定义对象,也不会更改默认常量定义点
然而,有些方法的明确目的是改变这三种上下文中的一种或多种
*\u eval
方法系列会更改self
(上下文1)和默认的定义对象(上下文2),但不会更改默认的常量定义点(上下文3)。特别是,所有*\u eval
(*\u exec
)方法都将self
设置到接收器。“code”>“instance”
versions将默认定义设置为接收器的singleton类、“code”>“module”
和class_*
版本将默认定义设置为接收器
但是,默认的常量定义点没有更改,因此常量定义(和查找)的工作原理与以前一样:定义转到最近的词汇性封闭模块定义,查找从最近的词汇性封闭模块定义开始,并通过继承以词汇性向外和动态向上进行
据我所知,唯一改变默认常量定义点的结构是模块
/类
定义。类似:@MarcinKołodziej似乎揭示了这个问题,词法范围在这里很重要。我们似乎需要一个显式的类
关键字来打开新的作用域。这真的很疯狂,但想当然地认为定义方法
也能起到类似的作用