【发布时间】:2014-07-24 22:18:42
【问题描述】:
module Kernel
undef rand
undef srand
end
为什么 Kernel.rand 还在工作?我知道该方法是在 Kernel.singleton_class 上调用的,但是单例类包括未定义方法的内核模块。这只是红宝石黑魔法的一个例子,还是我从红宝石对象模型中遗漏了一些东西?谢谢!
【问题讨论】:
标签: ruby
module Kernel
undef rand
undef srand
end
为什么 Kernel.rand 还在工作?我知道该方法是在 Kernel.singleton_class 上调用的,但是单例类包括未定义方法的内核模块。这只是红宝石黑魔法的一个例子,还是我从红宝石对象模型中遗漏了一些东西?谢谢!
【问题讨论】:
标签: ruby
尽管像这样对现有的主模块进行猴子补丁是一种非常糟糕的做法,尤其是 Kernel,它是 Ruby 中每个 object 的祖先。它可能在您或其他项目中产生非常糟糕和严重的影响,它包括在内。虽然为了理解上面的代码发生了什么,您可以更改:
module Kernel
undef rand
undef srand
end
到
module Kernel
class << self
undef rand
undef srand
end
end
# OR, much cleaner:
Kernel.instance_eval { undef :rand, :srand }
通过class << self,您正在劫持内核的单例类/特征类,并在那里取消定义单例方法rand 和srand。
这些是内核的单例方法而不是实例方法。
原来:
Kernel.singleton_methods.grep(/rand/)
=> [:srand, :rand]
之后:
Kernel.singleton_methods.grep(/rand/)
=> []
类/模块中的我知道该方法是在 Kernel.singleton_class 上调用的,但是单例 类包括内核模块,其方法未定义。
include 不包括该类/模块中的 单例方法。 rand 和 srand 仅在内核的 singleton_class 中有效定义,并且只能从那里取消定义。
更新:
如前所述,顶级对象(main) 中的rand 和srand 仍然有效,但Kernel.rand 和Kernel.srand 将给出NoMethodError。原因:
当一个模块包含在一个类或其他模块中时,Ruby 内部会创建一个特殊的 include 类,它保存当时定义的模块的引用,并添加类的祖先链中的那个类(虽然是隐藏的)。
现在,Object 之前已经包含 Kernel。该实例的内核中存在的方法堆叠在隐藏的包含类中,并且可用于Object。这就是为什么main(Object 的实例)仍然持有这些方法的引用。
这就是为什么rand 有效而Kernel.rand 无效的原因。
【讨论】:
rand 在顶级(主)对象中仍然有效,但Kernel.rand 不再起作用。这是令人惊讶的。调查它...
include 模块的原因。 :)