嵌套的单例类方法查找

Dim*_*ets 10 ruby inheritance dynamic-dispatch object-model

首先,我明白这个问题在现实世界中没有应用,我只是好奇.

想象一下,我们有一个使用单例方法的类:

class Foo
    def self.bar
    end
end
Run Code Online (Sandbox Code Playgroud)

如果我们调用Foo.bar它,它将首先在每个祖先的单例类中搜索一个方法Foo,然后查看.class方法及其祖先引用的类.我们可以确认,Foo.singleton_class.ancestors返回:

[#<Class:Foo>, #<Class:Object>, #<Class:BasicObject>,
 Class, Module, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

但是如果我们有一个嵌套的单例类会发生什么,比如:

class Foo
  class << self
    class << self
      def bar
      end
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

如果我们打电话Foo.singleton_class.singleton_class.ancestors,它会返回:

[#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>,
 #<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>,
 #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

我不明白这个层次结构是如何组织的.

Kri*_*ján 18

大部分解释都是基于James Coglan的Ruby方法调度工作原理,这是Ruby Hacking Guide的一小部分,而且只是一个源代码.

从摘要开始,祖先看起来像这样:

                                                           +----------------+
                                                           |                |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>         |
|                              ^                           ^                |
|                              |                           |                |
|                            Class ~~~~~~~~~~~~~~~> #<Class:Class>          |
|                              ^                           ^                |
|                              |                           |                |
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> |
|     ^                        ^                           ^                |
|     |        Kernel          |                           |                |
|     |          ^             |                           |                |
|     |          |             |   +-----------------------|----------------+
|     +-----+----+             |   |                       |
|           |                  |   v                       |
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>>
            ^                  ^                           ^
            |                  |                           |
           Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>>

---> Parent
~~~> Singleton class
Run Code Online (Sandbox Code Playgroud)

让我们从头开始构建.BasicObject是一切的根源 - 如果你检查BasicObject.superclass,你得到nil.BasicObject也是一个例子Class.是的,这是循环的,并且代码中有一个特殊情况来处理它.什么时候,是一个孩子A的实例,所以我们得到这个:BA.singleton_classB

                           Class
                             ^
                             |
BasicObject ~~~~~> #<Class:BasicObject>
Run Code Online (Sandbox Code Playgroud)

Object继承自BasicObject.当A继承自B,A是孩子B并且A.singleton_class是孩子的时候B.singleton_class.Object还包括Kernel.当A包含时B,B插入作为A(A自身之后,但之前A.superclass)的第一个祖先.

                           Class
                             ^
                             |
BasicObject ~~~~~> #<Class:BasicObject
    ^                        ^
    |        Kernel          |
    |          ^             |
    |          |             |
    +-----+----+             |
          |                  |
        Object ~~~~~~> #<Class:Object>
Run Code Online (Sandbox Code Playgroud)

Kernel是一个实例Module.它是Module我们唯一看到的实例,它的单例类没有出现在任何祖先链中,所以我不会超越它.

现在我们归结为Foo,继承自Object(虽然你不需要写< Object).我们已经可以弄清楚Foo它和它的单身类是什么了.

                           Class
                             ^
                             |
BasicObject ~~~~~> #<Class:BasicObject>
    ^                        ^
    |        Kernel          |
    |          ^             |
    |          |             |
    +-----+----+             |
          |                  |
        Object ~~~~~~> #<Class:Object>
          ^                  ^
          |                  |
         Foo ~~~~~~~~> #<Class:Foo>
Run Code Online (Sandbox Code Playgroud)

现在Class继承ModuleModule继承Object,所以添加Module和适当的单例类.由于Module < ObjectObject < BasicObjectBasicObject.instance_of?(Class),这是那里的绘图变得有点古怪.请记住,只要你击中就停止向上移动BasicObject.

                                                           +----------------+
                                                           |                |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>         |
|                              ^                           ^                |
|                              |                           |                |
|                            Class ~~~~~~~~~~~~~~~> #<Class:Class>          |
|                              ^                                            |
|                              |                                            |
| BasicObject ~~~~~> #<Class:BasicObject>                                   |
|     ^                        ^                                            |
|     |        Kernel          |                                            |
|     |          ^             |                                            |
|     |          |             |   +----------------------------------------+
|     +-----+----+             |   |
|           |                  |   v
+-------> Object ~~~~~~> #<Class:Object>
            ^                  ^
            |                  |
           Foo ~~~~~~~~> #<Class:Foo>
Run Code Online (Sandbox Code Playgroud)

最后一步.每个实例Class都有一个singleton_class(虽然它不会被实例化,直到它需要,否则你需要更多的RAM).我们所有的单例类都是实例Class,所以它们都有单例类.注意这句话:一个类的单例类的父类是类的父类的单例类.我不知道是否有一种简洁的方式来表明,就类型系统而言,Ruby源代码几乎都说它只是为了保持一致性.因此,当你要求时Foo.singleton_class.singleton_class,语言会愉快地迫使你向上传播必要的父母,最后导致:

                                                           +----------------+
                                                           |                |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>         |
|                              ^                           ^                |
|                              |                           |                |
|                            Class ~~~~~~~~~~~~~~~> #<Class:Class>          |
|                              ^                           ^                |
|                              |                           |                |
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> |
|     ^                        ^                           ^                |
|     |        Kernel          |                           |                |
|     |          ^             |                           |                |
|     |          |             |   +-----------------------|----------------+
|     +-----+----+             |   |                       |
|           |                  |   v                       |
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>>
            ^                  ^                           ^
            |                  |                           |
           Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>>
Run Code Online (Sandbox Code Playgroud)

如果你从这个图中的任何节点开始并且从深度优先,从右到左遍历(并且停在BasicObject那里,你得到节点的祖先链,就像我们想要的那样.而且,我们已经从一些基本公理构建了它,所以我们可能只是能够信任它.缺乏信任,有一些有趣的方法可以进一步验证结构.

尝试查看图中node.singleton_class.ancestors - node.ancestors的任何节点.这给了我们单例类的祖先,它们不是节点本身的祖先,这消除了列表中一些令人困惑的冗余.

> Foo.singleton_class.singleton_class.ancestors - Foo.singleton_class.ancestors
 => [#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>,
     #<Class:Class>, #<Class:Module>]
Run Code Online (Sandbox Code Playgroud)

您也可以验证任何一个父母node.superclass.

> Foo.singleton_class.singleton_class.superclass
 => #<Class:#<Class:Object>>
Run Code Online (Sandbox Code Playgroud)

您甚至可以验证对象标识是否一致,因此没有匿名类在整个地方弹出,彼此之间没有特定的关系.

> def ancestor_ids(ancestors)
>   ancestors.map(&:object_id).zip(ancestors).map{|pair| pair.join("\t")}
> end

> puts ancestor_ids(Foo.ancestors)
70165241815140  Foo
70165216040500  Object
70165216040340  Kernel
70165216040540  BasicObject

> puts ancestor_ids(Foo.singleton_class.ancestors)
70165241815120  #<Class:Foo>
70165216039400  #<Class:Object>
70165216039380  #<Class:BasicObject>
70165216040420  Class
70165216040460  Module
70165216040500  Object # Same as Foo from here down
70165216040340  Kernel
70165216040540  BasicObject

> puts ancestor_ids(Foo.singleton_class.singleton_class.ancestors)
70165241980080  #<Class:#<Class:Foo>>
70165215986060  #<Class:#<Class:Object>>
70165215986040  #<Class:#<Class:BasicObject>>
70165216039440  #<Class:Class>
70165216039420  #<Class:Module>
70165216039400  #<Class:Object> # Same as Foo.singleton_class from here down
70165216039380  #<Class:BasicObject>
70165216040420  Class
70165216040460  Module
70165216040500  Object
70165216040340  Kernel
70165216040540  BasicObject
Run Code Online (Sandbox Code Playgroud)

而且,简而言之,就是你如何狙击书呆子.