包含/扩展内核不会在 main:Object 上添加这些方法 [英] Including/Extending the Kernel doesn't add those methods on main:Object

查看:44
本文介绍了包含/扩展内核不会在 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的单例类中(KernelModule类的对象).这就是为什么你只能在 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.includeKernelKernel 方法表的链接,当你改变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 的方法,因为它不知道KernelincludeM 的继承链,它只知道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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆