包含/扩展内核不会在 main:Object 上添加这些方法 [英] Including/Extending the Kernel doesn't add those methods on main:Object
问题描述
我正在尝试将一个方法添加到 Kernel
模块中,但我没有重新打开 Kernel
并直接定义一个实例方法,而是编写了一个模块和我希望 Kernel
能够扩展/包含
那个模块.
I'm trying to add a method into the Kernel
module, but instead of reopening the Kernel
and directly defining an instance method, I'm writing a module and I want Kernel
to extend/include
that module.
module Talk
def hello
puts "hello there"
end
end
module Kernel
extend Talk
end
当我在 IRB 中运行时:
When I run this in 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>
如果我检查 Kernel
上的 instance_methods
,我可以看到 #hello 已添加到 Kernel
,但未添加到 main对象
.
If I check the instance_methods
on Kernel
, I can see #hello was added to the Kernel
, but not in main Object
.
我也试过使用 include
,但同样的事情发生了:
I've also tried using include
, but the same thing happens:
module Kernel
include Talk
end
但是,如果我直接定义:
However, if I define it directly:
module Kernel
def hello
puts "hello there"
end
end
然后它确实包含在main Object
中.
$ hello
hello there
=> nil
在 Object
中包含 Talk
模块也可以:
Including the Talk
module in Object
works too:
class Object
include Talk
end
也许我做错了,或者我遗漏了一些简单的东西,但这种行为让我感到困惑.
Perhaps I'm doing this wrong, or I'm missing something simple, but this behavior is confusing me.
推荐答案
我会试着更深入地解释一下:
I will try to explain it a little bit deeper:
当你include
模块到某个类中时,Ruby 会创建特殊的内部include 类 并将其添加到层次结构中(请注意,基本上你是不允许看到包含类来自Ruby程序,它是隐藏类):
when you include
module into some class, Ruby creates special internal include class and adds it to the hierarchy (please note that basically you are not allowed to see include class from Ruby programm, it is hidden class):
Given A inherits B
And we have a module C
When A includes C
Then A inherits includeC inherits B
如果包含的模块有其他包含的模块,那么也会为这些模块创建 includeModules:
If included module has other included Modules, then includeModules will be created for these modules as well:
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
包含类 C 方法表是一个链接到原始类 C 的方法表.
Include class C method table is a link to method table of original class C.
当你用一个模块扩展
某个对象时,这个模块被包含在这个对象的单例类中,因此:
When you extend
some object with a module, then this module is included into singleton class of this object, thus:
class << self; include C; end
# is the same as
extend C
转到您的示例:
module Kernel
extend Talk
end
这里将Talk
模块包含在Kernel
的单例类中(Kernel
是Module
类的对象).这就是为什么你只能在 Kernel 对象上调用 hello
方法:Kernel.hello
.
Here you include Talk
module into singleton class of Kernel
(Kernel
is an object of class Module
). This is why you can call hello
method only on Kernel object: Kernel.hello
.
如果我们这样写:
module Kernel
include Talk
end
然后 Kernel
将在内部继承 include 类 includeTalk(带有 Talk
方法链接的类).
Then Kernel
will internally inherit include class includeTalk (class with link to Talk
methods).
但是内核模块已经包含在Object
中——Object
继承了它自己的includeKernel类并且includeKernel类有链接到Kernel
的方法表和没有看到 Kernel
的新包含类的方法.
But Kernel module is already included into Object
- Object
inherits its own includeKernel class and includeKernel class has link to method table of Kernel
and does not see methods of new include classes of Kernel
.
但是现在如果您将内核重新包含到对象中,所有对象都会看到 Talk 的方法:
But now if you will re-include Kernel into Object, all Objects will see methods of 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
您的问题的解决方案可能是使用您的新模块扩展主对象:
The solution for you probjem might be to extend main object with your new module:
extend Talk
希望这能澄清您观察到的一些行为:)
Hope this is clarified a bit behaviour you observe :)
更新
将尝试澄清您的问题:
我仍然有点困惑为什么我必须在 Object 中重新包含内核.在不涉及主对象的情况,我可以实例化一个对象基于一个类,然后重新打开该类并包含一个模块,该对象将在我的模块中看到方法.有没有主对象如何包含内核的不同之处?我是也不确定对象继承其自己的 includeKernel"是什么意思class 和 includeKernel 类..."为什么没有看到新的包含内核中的模块?
I'm still a bit confused why I have to re-include Kernel in Object. In cases that don't involve the main Object, I can instantiate an object based on a class and then later reopen that class and include a module, and that object will see methods in my module. Is there something different about how main Object includes the Kernel? I'm also not sure what you mean by "Object inherits its own includeKernel class and includeKernel class..." Why doesn't it see the new included module in Kernel?
您讲述了将模块直接包含到对象的类中的情况:
You tell about the case with direct inclusion of module into class of an object:
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
在这种情况下,您将拥有 C
类的对象 c
.C
类继承了Object
(因为它是Class
类的实例.c 在他的单例类 -> 然后在他的类 C -> 然后在类 C 的父类中(在本例中为 Object
实例方法).当我们将模块 M
包含到类 中时C
,那么 includeM
将是 C
的超类,如果 c
在他的单例类中找不到他的实例方法并且 C
类,它会在 includeM
中搜索实例方法.includeM
有一个链接到 M
类的方法表(Module
类的实例.因此,当 c
搜索实例方法 hi
时,它会在 M
模块中找到它.
in this case you will have object c
of class C
. Class C
inherits Object
(because it is an instance of Class
class. c looks for its instance methods in his singleton class -> then in his class C -> then in parents of class C (in this case Object
instance methods). When we include module M
into class C
, then includeM
will be a superclass of C
and if c
will not find his instance method in his singleton class and C
class, it will search for instance methods in includeM
. includeM
has a link to method table of M
class (instance of Module
class). Thus when c
search for instance method hi
it finds it in the M
module.
但这与将 M
模块包含在 Kernel
模块中的情况不同.在程序开始时Object
类包含模块Kernel
: class Object;包括内核;结束
.这就是为什么我说Object
继承自includeKernel
.includeKernel
有Kernel
方法表的链接,当你改变Kernel 方法表时,includeKernel
也会看到这些变化:
But this is different from case when you include module M
into Kernel
module.
At the program start Object
class includes module Kernel
: class Object; include Kernel; end
. This is why I say that Object
inherits from includeKernel
. includeKernel
has link to method table of Kernel
and when you change method table of Kernel, includeKernel
will also see these changes:
module Kernel
def hi # add hi method to method table of Kernel
puts 'hi'
end
end
hi # => hi # any Object now see method hi
但是当你将模块 M 包含到 Kernel 中时,Kernel 的方法表不会改变.相反,内核现在将继承 includeM
include 类.includeKernel
看不到includeM
的方法,因为它不知道Kernel
和includeM
的继承链,它只知道Kernel
的方法表.
But when you include module M into Kernel, then method table of Kernel is not changed. Instead Kernel will now inherit includeM
include class. includeKernel
don't see methods of includeM
because it does not know about inheritance chain of Kernel
and includeM
, it only knows the method table of Kernel
.
但是当你将 Kernel
重新包含到 Object
中时,包含机制会看到 Kernel
包含 M
和也会为 Object
创建 includeM.现在Object
将继承includeKernel
将继承includeM
将继承BasicObject
.
But when you re-include Kernel
into Object
, inclusion mechanism will see that Kernel
includes M
and will create includeM for Object
as well. Now Object
will inherit includeKernel
will inherit includeM
will inherit BasicObject
.
这篇关于包含/扩展内核不会在 main:Object 上添加这些方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!