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

6 ruby object-model

我读过关于这一主题的帖子计算器以及几篇文章,其中包括入门Ruby的方法查找,什么是Ruby的方法查找路径.另外,我查看了Ruby Metaprogramming 2中的对象模型章节,在几个聊天室中询问,并制作了这个reddit线程.没学习C,我已经尽我所能来解决这个问题.

如上面的资源所述,在接收对象(如fido_instance)上进行方法查找时,会检查这6个位置(按顺序):

  1. 单身类的fido_instance
  2. IClass(来自扩展模块)
  3. IClass(来自前置模块)
  4. IClass(来自附带的模块)
  5. 超类(如果在此处找不到方法,请重复步骤4-6)

显然,该图是不完整的,并且所有这些单例类可能都不是在现实世界中创建的.尽管如此,这6个步骤还有很多不足之处,并未涉及以下情况.如果在单例类之上没有扩展/前置IClass fido_instance,那么就没有解释第4步是否在单例类上执行fido_instance.我不得不假设,因为整个方法查找会短路.

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

  1. 检查fido_instance.class方法.(显然,ruby不会使用自己的#class方法来进行方法查找,但它传达了进程的逻辑)
  2. 检查fido_instance.class.superclass方法.继续添加.superclass并检查方法,直到没有剩余超类.(同样,ruby不会使用自己的#superclass方法)
  3. 找不到方法.从步骤1开始,这次寻找#method_missing.

我还记得读过如果接收对象是一个类,有一个单独的方法查找过程,但我不记得在哪里.

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

And*_*rtz 2

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

fido_instance.singleton_class.ancestors

这将始终为您提供 Ruby 使用的方法查找顺序。当你这样看时,这很简单,这就是你问题的底线答案。Ruby 将从 singleton_class 开始,向上查找该方法的祖先。使用你的图表:

fido.singleton_class.ancestors
=> [Fetch, WagTail, DogClass, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

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

(注2:如果一直到 都没有找到BasicObject,那么它将调用method_missing相同的祖先链。)

调用类上的方法时没有什么不同,因为在 Ruby 中,类只是 class 的一个实例Class。因此,就像以前一样,将继续搜索其祖先链DogClass.method1method1DogClass.singleton_class

DogClass.singleton_class.ancestors
=> [Bark, Class, Module, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

既然您使用extendBark,我们就在这里找到它!因此,如果Bark定义了一个方法bark,那么您可以调用DogClass.bark,因为该方法是在DogClass“singleton_class”祖先中定义的。

要了解祖先树是什么(而不是每次都依赖打印出来),您只需要知道如何通过子类化、extendincludeprepend等来修改祖先。

  1. 子类化为子类提供了其超类的整个祖先链。
  2. include在类中添加模块C会将该模块添加到祖先链中,位于C其他所有模块之后和之前。
  3. prepend在类中添加模块C会将该模块添加到祖先链中的所有内容之前,包括C任何当前前置的模块。
  4. def x.method1添加method1x.singleton_class. 类似地,x.extend(M)将添加到(但不是)M的祖先。请注意,后者正是 和 所发生的情况,但同样适用于任何对象。x.singleton_classx.classBarkDogClass.singleton_class

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


切线:

上面关于类方法的内容是我理解单例类对于 Ruby 的重要性的关键。你显然不能定义barkon DogClass.class,因为DogClass.class == Class我们不想要barkon Class!那么我们如何才能允许DogClass成为 的实例Class,从而允许它具有barkDogClass但不相关的类定义的(类)方法呢?使用单例类!通过这种方式,定义一个“类方法”,就像在def self.xclass 内部一样C,有点像C.singleton_class.send(:define_method, :x) {...}.