嵌套单例类方法查找

11

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

假设我们有一个带有单例方法的类:

class Foo
    def self.bar
    end
end

如果我们调用Foo.bar,它首先会在每个祖先类的单例类中搜索一个方法,然后再在由.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]

我不理解这个层次结构是如何组织的。


你假设它已经被组织好了。你确定它已经被组织好了吗? - ekampp
也许您可以澄清一下您的问题?您是在问第二个示例中的bar方法定义在哪里吗? - saghaulor
定义了bar方法。我想知道第二个例子中继承层次结构的组织方式。 - Dima Knivets
2个回答

19

这篇解释的很多内容都基于 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

让我们从头开始构建。 BasicObject 是所有东西的根 - 如果你检查 BasicObject.superclass,你会得到 nilBasicObject 也是一个 Class 的实例。是的,这变成了一个循环,有一个 代码中的特殊情况 处理它。当 AB 的实例时,A.singleton_classB 的子类,因此我们得到:

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

Object继承自BasicObject。当AB继承时,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 继承自 Module,而 Module 继承自 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

简而言之,这就是如何 狙击书呆子


3

#<Class:Foo> 是给定类 Foo 的特殊/匿名类。如果该特殊/匿名类也被扩展了,那么另一个特殊类就会被创建 - 因此表示为 #<Class:#<Class:Foo>>

特殊类的父类是 Object 类的特殊类,其父类是 BasicObject 的特殊类。同样,另一个特殊类的特殊类的父类是 Object 类的特殊类,以此类推。

下面的代码与这个解释结合起来可以更深入地理解

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]

以上所见的Eigen类层次结构可以重复到任意级别。例如:
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]

干得好。我本来想回答这个问题,但在开始之前我想先搞清楚术语。你比我快了。这就是我在这里提出这些问题的原因:https://dev59.com/ClwZ5IYBdhLWcg3wJ9nN#VpEToYgBc1ULPQZFfhDQ回答方法定义的位置需要解释继承层次结构。 - saghaulor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接