我想知道以下两个模块之间的区别
# First Example module Parent module Child end end
以及
# Second Example module Parent::Child end
使用第二种方法,看起来好像父模块必须预先定义,否则我会得到一个“未初始化常量”错误既然如此,定义这样的模块,然后添加语法和文件结构(如文件夹等)方面的嵌套子模块的首选方法是什么?参考Rails方法将非常感谢。这两个示例在所有意图和目的上是否等同?
yduiuuwa1#
在第一个示例中,它定义了Parent模块,然后定义了Child模块。第二个示例,正如您自己所说的,必须事先定义Parent模块。以多一行代码为代价,您可以确保使用第一个示例嵌套的模块总是要定义的。以Rails为例,让我们看一下 railties/lib/rails/engine.rb 文件,该文件重新打开Rails模块,然后在其中定义一个Engine类。
Parent
Child
Rails
Engine
class Rails::Engine
但是,可能是由于上面提到的原因,也可能是为了清楚起见,首先定义了模块,然后定义了其中的类。
sulc1iza2#
我更喜欢第二种方法(如果我确定已经定义了Parent),因为它看起来更干净,特别是当嵌套很深的时候。然而,第一种方法有一些优点,一个尚未讨论的是嵌套模块可以访问封装模块中任何词法上可用的常量。
0ejtzxu13#
一般来说,除非你能完全确定父模块已经存在,否则你不想使用模块Parent::Child语法定义一个模块。子模块只能使用::语法。在您的示例中,如果执行以下操作,则不会出现未初始化常量错误。
module Parent end module Parent::Child end
pieyvz9o4#
看来,班尼斯特的回答也是这种行为的原因:
ruby-1.9.2-p290 :001 > module A; module A; A.ancestors; end; end => [A::A] ruby-1.9.2-p290 :002 > module A::A; A.ancestors; end => [A]
内模A是外模A内部的一个常数,因此使用第二种方法是不可见的。编辑我以前的评论:这在《Ruby编程语言》(第一版)的7.9“常量查找”中有解释,这里相关的部分是Module.nesting,它不包含第二个例子中的外部模块,因此A只能在全局范围内找到。
68bkxrlz5#
第二个版本(A::B)与第一个版本的区别有三个方面:1.它假设外部模块A已经定义,如果没有,Ruby将抛出NameError: uninitialized constant A错误,因此只有在确定模块A存在时才使用此语法。1.它可以让你减少缩进量。而不是3级缩进,只有一个是必要的。1.如果没有作用域解析(::)运算符,就不能访问B模块中A模块中定义的常量。下一节将详细介绍它。
(A::B)
A
NameError: uninitialized constant A
::
B
常量访问的作用域根据嵌套模块的定义而不同,让我们考虑一下第一个版本。
module A Z = 10 module B puts Z end end # output 10
让我们试着在使用第二种语法定义的模块中访问常量,它抛出一个NameError。
NameError
module A Z = 10 end module A::B puts Z end # output uninitialized constant A::B::Z (NameError)
在这里,您必须使用作用域解析操作符(::)访问它,如下所示:
module A Z = 10 end module A::B puts A::Z end # output 10
Module.nesting方法返回调用时嵌套的模块列表。下面的示例显示两个版本在模块嵌套方面的不同。
Module.nesting
module A p Module.nesting # [A] module B p Module.nesting # [A::B, A] end module B::C p Module.nesting # [A::B::C, A] end end module A::D p Module.nesting # [A::D] end
当模块D被定义时,嵌套不包括模块A。因此,模块D不能访问在模块A中定义的常量,除非显式地使用作用域解析(::)运算符。
D
5条答案
按热度按时间yduiuuwa1#
在第一个示例中,它定义了
Parent
模块,然后定义了Child
模块。第二个示例,正如您自己所说的,必须事先定义Parent
模块。以多一行代码为代价,您可以确保使用第一个示例嵌套的模块总是要定义的。以Rails为例,让我们看一下 railties/lib/rails/engine.rb 文件,该文件重新打开
Rails
模块,然后在其中定义一个Engine
类。但是,可能是由于上面提到的原因,也可能是为了清楚起见,首先定义了模块,然后定义了其中的类。
sulc1iza2#
我更喜欢第二种方法(如果我确定已经定义了Parent),因为它看起来更干净,特别是当嵌套很深的时候。
然而,第一种方法有一些优点,一个尚未讨论的是嵌套模块可以访问封装模块中任何词法上可用的常量。
0ejtzxu13#
一般来说,除非你能完全确定父模块已经存在,否则你不想使用模块Parent::Child语法定义一个模块。子模块只能使用::语法。在您的示例中,如果执行以下操作,则不会出现未初始化常量错误。
pieyvz9o4#
看来,班尼斯特的回答也是这种行为的原因:
内模A是外模A内部的一个常数,因此使用第二种方法是不可见的。
编辑我以前的评论:
这在《Ruby编程语言》(第一版)的7.9“常量查找”中有解释,这里相关的部分是Module.nesting,它不包含第二个例子中的外部模块,因此A只能在全局范围内找到。
68bkxrlz5#
第二个版本
(A::B)
与第一个版本的区别有三个方面:1.它假设外部模块
A
已经定义,如果没有,Ruby将抛出NameError: uninitialized constant A
错误,因此只有在确定模块A存在时才使用此语法。1.它可以让你减少缩进量。而不是3级缩进,只有一个是必要的。
1.如果没有作用域解析(
::
)运算符,就不能访问B
模块中A
模块中定义的常量。下一节将详细介绍它。访问嵌套模块中的常量
常量访问的作用域根据嵌套模块的定义而不同,让我们考虑一下第一个版本。
让我们试着在使用第二种语法定义的模块中访问常量,它抛出一个
NameError
。在这里,您必须使用作用域解析操作符(
::
)访问它,如下所示:模块嵌套
Module.nesting
方法返回调用时嵌套的模块列表。下面的示例显示两个版本在模块嵌套方面的不同。当模块
D
被定义时,嵌套不包括模块A
。因此,模块D
不能访问在模块A
中定义的常量,除非显式地使用作用域解析(::
)运算符。