我想添加一个方法到Kernel
模块,但不是重新打开Kernel
,直接定义一个实例方法,我正在写一个模块,我想Kernel
给extend/include
该模块.
module Talk
def hello
puts "hello there"
end
end
module Kernel
extend Talk
end
Run Code Online (Sandbox Code Playgroud)
当我在IRB中运行时:
$ hello
NameError: undefined local variable or method `hello' for main:Object
from (irb):12
from /Users/JackC/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>
Run Code Online (Sandbox Code Playgroud)
如果我检查instance_methods
上Kernel
,我可以看到#hello加入了Kernel
,但不是在main Object
.
我也尝试过使用include
,但同样的事情发生了:
module Kernel
include Talk
end
Run Code Online (Sandbox Code Playgroud)
但是,如果我直接定义它:
module Kernel
def hello
puts "hello there"
end
end
Run Code Online (Sandbox Code Playgroud)
然后它确实包含在main Object
.
$ hello
hello there
=> nil
Run Code Online (Sandbox Code Playgroud)
包括Talk
模块也在Object
工作中:
class Object
include Talk
end
Run Code Online (Sandbox Code Playgroud)
也许我做错了,或者我错过了一些简单的事情,但这种行为让我感到困惑.
Ali*_*kau 13
我会尝试更深入地解释一下:
当你include
模块化到某个类时,Ruby会创建特殊的内部包含类并将其添加到层次结构中(请注意,基本上你不允许从Ruby程序中看到include类,它是隐藏类):
Given A inherits B
And we have a module C
When A includes C
Then A inherits includeC inherits B
Run Code Online (Sandbox Code Playgroud)
如果包含的模块包含其他模块,那么也将为这些模块创建includeModules:
Given A inherits B
And we have a module C
And C includes module D
When A includes C
Then A inherits includeC inherits includeD inherits B
Run Code Online (Sandbox Code Playgroud)
包含类 C方法表是原始类C的方法表的链接.
当你extend
有一个模块的对象,那么这个模块被包含在这个对象的单例类中,因此:
class << self; include C; end
# is the same as
extend C
Run Code Online (Sandbox Code Playgroud)
举个例子:
module Kernel
extend Talk
end
Run Code Online (Sandbox Code Playgroud)
在这里,您将Talk
模块包含到singleton类中Kernel
(Kernel
是类的对象Module
).这就是为什么你hello
只能在Kernel对象上调用方法:Kernel.hello
.
如果我们这样写:
module Kernel
include Talk
end
Run Code Online (Sandbox Code Playgroud)
然后Kernel
将内部继承包括类 includeTalk(带有Talk
方法链接的类).
但是内核模块已经包含在内Object
- Object
继承自己的includeKernel类,includeKernel类有方法表的链接Kernel
,并没有看到新包含类的方法Kernel
.
但是现在如果你将内核重新包含到Object中,所有对象都会看到Talk的方法:
> module Talk
> def hi
> puts 'hi'
> end
> end
=> nil
> module Kernel
> include Talk
> end
=> Kernel
> hi
NameError: undefined local variable or method `hi` for main:Object
from (irb):9
from /usr/share/ruby-rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>`
> class Object
> include Kernel
> end
=> Object
> hi
hi
=> nil
Run Code Online (Sandbox Code Playgroud)
您的问题的解决方案可能是使用新模块扩展主对象:
extend Talk
Run Code Online (Sandbox Code Playgroud)
希望这可以澄清你观察到的一点行为:)
UPDATE
将尝试澄清您的问题:
我仍然有点困惑为什么我必须在对象中重新包含内核.在不涉及主Object的情况下,我可以基于类实例化一个对象,然后重新打开该类并包含一个模块,该对象将在我的模块中看到方法.关于主要对象如何包含内核有什么不同吗?我也不确定你的意思是"Object继承了自己的includeKernel类和includeKernel类......"为什么它没有在内核中看到新包含的模块?
您可以通过将模块直接包含在对象的类中来说明这种情况:
module M
def hi
puts 'hi'
end
end
class C
end
c = C.new
c.hi # => UndefinedMethod
class C
include M
end
c.hi # => hi
Run Code Online (Sandbox Code Playgroud)
在这种情况下,你将拥有c
类的对象C
.类C
继承Object
(因为它是一个实例的Class
类C相对于它的实例方法在他的单例类- >然后在他的C类- >然后在C级的父母(在这种情况下Object
实例方法)当我们包括模块.M
成class C
,然后includeM
将是一个超类,C
如果c
在他的单例类和C
类中找不到他的实例方法,它将搜索实例方法includeM
.includeM
有一个M
类的方法表的链接(类的实例Module
).因此当c
搜索实例方法时hi
它在M
模块中找到它.
但这与将模块包含M
到Kernel
模块中的情况不同.在程序启动Object
类包括模块Kernel
:class Object; include Kernel; end
.这就是我说Object
继承自的原因includeKernel
.includeKernel
有方法表的链接Kernel
,当你更改内核的方法表时,includeKernel
也会看到这些变化:
module Kernel
def hi # add hi method to method table of Kernel
puts 'hi'
end
end
hi # => hi # any Object now see method hi
Run Code Online (Sandbox Code Playgroud)
但是当你将模块M包含到内核中时,内核的方法表不会改变.相反,内核现在将继承includeM
include类.includeKernel
看不到的方法includeM
,因为它不知道的继承链Kernel
和includeM
,它只知道的方法表Kernel
.
但是当你重新Kernel
加入时Object
,包含机制将会看到Kernel
包含M
并将创建includeM Object
.现在Object
将继承includeKernel
将继承includeM
将继承BasicObject
.