嵌套的单例类方法查找

时间:2015-08-02 19:10:13

标签: ruby inheritance object-model dynamic-dispatch

首先,我知道这个问题在现实世界中没有应用,我只是好奇。

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

class Foo
    def self.bar
    end
end

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

[#<Class:Foo>, #<Class:Object>, #<Class:BasicObject>,
 Class, Module, Object, Kernel, BasicObject]

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

class Foo
  class << self
    class << self
      def bar
      end
    end
  end
end

如果我们致电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]

我不明白这种层次结构是如何组织的。

2 个答案:

答案 0 :(得分:18)

这些解释大部分基于詹姆斯·科格兰的How Ruby Method Dispatch WorksRuby Hacking Guide的一点点,只是source的一小部分。

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

                                                           +----------------+
                                                           |                |
+--------------------------- 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

让我们从头开始构建。 BasicObject是所有内容的根源 - 如果您选中BasicObject.superclass,则会获得nilBasicObject也是Class的一个实例。是的,这是循环的,并且有special case in the code来处理它。当AB的实例时,A.singleton_classB的孩子,所以我们得到了这个:

                           Class
                             ^
                             |
BasicObject ~~~~~> #<Class:BasicObject>

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

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

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

现在我们归结为Foo,它继承自Object(尽管您不需要写< Object)。我们已经可以弄清楚Foo及其单例类是什么的孩子了。

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

现在Class继承自ModuleModule继承自Object,因此请添加Module和相应的单例类。由于Module < ObjectObject < BasicObject以及BasicObject.instance_of?(Class),这是绘图变得有点时髦的地方。请记住,只要您点击BasicObject,就停止向上移动。

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

最后一步。 Class的每个实例都有一个singleton_class(虽然它不会被实例化,直到需要它,否则你需要更多的RAM)。我们所有的单例类都是Class的实例,因此它们具有单例类。注意这句话:一个班级的单身人士的父母是班级的父母的单身人士。我不知道是否有一种简洁的方式来表明,就类型系统而言,Ruby source几乎都说它只是为了保持一致性。所以,当你要求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>>

如果您从此图表中的任何节点开始并从深度优先,从右到左(并在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>]

您还可以使用node.superclass验证任何一位父级。

> Foo.singleton_class.singleton_class.superclass
 => #<Class:#<Class:Object>>

你甚至可以验证对象身份是否一致,所以没有匿名类在整个地方突然出现,彼此没有特别的关系。

> 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

简而言之,就是snipe a nerd

答案 1 :(得分:3)

#<Class:Foo>是给定类Foo的特征/匿名类。如果此特征/匿名类也已被扩展,则将创建另一个特征类 - 因此表示为#<Class:#<Class:Foo>>

特征类的父类是Object类的特征类,其父类是BasicObject的特征类。类似地,另一个特征类的特征类的父类是Object类的特征类的特征类,依此类推。

以下代码耦合with this explanation提供了更多见解

p Foo.class
p Foo.class.ancestors
puts "-----------------"
p Foo.singleton_class
p Foo.singleton_class.ancestors
puts "-----------------"
p Foo.singleton_class.singleton_class
p Foo.singleton_class.singleton_class.ancestors

输出

Class
[Class, Module, Object, Kernel, BasicObject]
-----------------
#<Class:Foo>
[#<Class:Foo>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
-----------------
#<Class:#<Class:Foo>>
[#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

如上所示的特征类层次结构将重复到任意数量的级别。 例如:

p Foo.singleton_class.singleton_class.singleton_class.singleton_class.singleton_class
puts "-----------------"
p Foo.singleton_class.singleton_class.singleton_class.singleton_class.singleton_class.ancestors

以上代码输出

#<Class:#<Class:#<Class:#<Class:#<Class:Foo>>>>>
-----------------
[#<Class:#<Class:#<Class:#<Class:#<Class:Foo>>>>>, #<Class:#<Class:#<Class:#<Class:#<Class:Object>>>>>, #<Class:#<Class:#<Class:#<Class:#<Class:BasicObject>>>>>, #<Class:#<Class:#<Class:#<Class:Class>>>>, #<Class:#<Class:#<Class:#<Class:Module>>>>, #<Class:#<Class:#<Class:#<Class:Object>>>>, #<Class:#<Class:#<Class:#<Class:BasicObject>>>>, #<Class:#<Class:#<Class:Class>>>, #<Class:#<Class:#<Class:Module>>>, #<Class:#<Class:#<Class:Object>>>, #<Class:#<Class:#<Class:BasicObject>>>, #<Class:#<Class:Class>>, #<Class:#<Class:Module>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]