ruby方法查找中的*实际*步骤是什么?

时间:2018-05-11 07:12:55

标签: ruby object-model

我已阅读有关此主题的stackoverflow帖子以及包含A Primer on Ruby Method LookupWhat is the method lookup path in Ruby的多篇文章。另外,我查看了 Ruby Metaprogramming 2 中的对象模型章节,在几个聊天室中询问,并制作了this reddit thread。没学习C,我已经做了我能做到的事情。

如上述资源所述,在fido_instance等接收对象的方法查找过程中,会检查这6个位置(按顺序):

  1. 单身类fido_instance
  2. IClass(来自扩展模块)
  3. IClass(来自前置模块)
  4. IClass(来自附带的模块)
  5. 超类(如果没有找到方法,重复步骤4-6)
  6. 显然,图表不完整,所有这些单例类可能都没有在现实世界中创建。尽管如此,这6个步骤仍然有很多不足之处,并没有涵盖以下场景。如果在fido_instance的单例类之上没有扩展/前置的IClass,则没有解释步骤4是否在fido_instance的单例类上执行。我不得不假设,因为整个方法查找会短路。

    如果我猜测一组可以解释ruby的方法查找行为的步骤,它可能看起来像:

    1. 检查fido_instance.class方法。 (显然,ruby不会使用自己的#class方法来进行方法查找,但它传达了流程的逻辑)
    2. 检查fido_instance.class.superclass方法。继续添加.superclass并检查方法,直到没有剩余超类。 (同样,ruby不会使用自己的#superclass方法)
    3. 方法尚未找到。从第1步开始,这次查找#method_missing。
    4. 我还记得读过,如果接收对象是一个类,那么会有一个单独的方法查找过程,但我无法回想起去哪里。

      那么,正确,详细的解释是什么并不涉及了解C?

1 个答案:

答案 0 :(得分:1)

我认为第二个参考文献中有一个...宝石...是答案的核心:单例类的祖先。应用于您的对象,它将是:

fido_instance.singleton_class.ancestors

这将始终为您提供Ruby使用的方法查找的顺序。当您以这种方式查看它时,这非常简单,这是您问题的底线答案。 Ruby将以singleton_class开头,并在寻找该方法的祖先之前逐步发展。使用图表:

fido.singleton_class.ancestors
=> [Fetch, WagTail, DogClass, Object, Kernel, BasicObject]

(注1:Bark不是此输出的一部分,因为您使用的是extend而不是include。稍后将详细介绍。)

(注意2:如果直到BasicObject都找不到它,它将在同一祖先链中调用method_missing。)

在类上调用方法时没有什么不同,因为在Ruby类中,它只是类Class的实例。因此,DogClass.method1将在method1上搜索DogClass.singleton_class,然后像以前一样向上搜索其祖先链。

DogClass.singleton_class.ancestors
=> [Bark, Class, Module, Object, Kernel, BasicObject]

由于您将extend用于Bark,因此我们可以在这里找到它!因此,如果Bark定义了方法bark,则可以调用DogClass.bark,因为该方法是在DogClass的singleton_class祖先中定义的。

要了解该祖先树是什么(而不是每次都依赖于打印出来),您只需要知道如何通过子类extendinclude,{{ 1}}等。

  1. 子类化为子类提供了其超类的整个祖先链。
  2. prepend在类include中添加模块,将其添加到祖先链中,位于C之后,然后再进行其他操作。
  3. C在类prepend中添加模块会将该模块添加到祖先链中,包括所有C和任何当前前置的模块。
  4. Cdef x.method1添加到method1。同样,x.singleton_classx.extend(M)添加到M的祖先(但不添加到x.singleton_class中)。请注意,后者正是x.classBark发生的情况,但同样可以应用于任何对象。

从上面的列表中删除DogClass.singleton_class,因为它不会修改对象的祖先链。它确实修改了该对象的extend的祖先-如我们所见,singleton_class已包含在Bark中。


切线:

上面关于类方法的一点是我了解单例类对Ruby的重要性的关键。您显然无法在DogClass.singleton_class.ancestors上定义bark,因为DogClass.class并且我们不希望在DogClass.class == Class上定义bark!那么,如何允许Class成为DogClass的实例,并允许它具有为Class定义的(类)方法bark却不是不相关的类呢?使用单例类!这样,像在类DogClass中的def self.x那样定义一个“类方法”就象C一样。