我已阅读有关此主题的stackoverflow帖子以及包含A Primer on Ruby Method Lookup,What is the method lookup path in Ruby的多篇文章。另外,我查看了 Ruby Metaprogramming 2 中的对象模型章节,在几个聊天室中询问,并制作了this reddit thread。没学习C,我已经做了我能做到的事情。
如上述资源所述,在fido_instance等接收对象的方法查找过程中,会检查这6个位置(按顺序):
显然,图表不完整,所有这些单例类可能都没有在现实世界中创建。尽管如此,这6个步骤仍然有很多不足之处,并没有涵盖以下场景。如果在fido_instance
的单例类之上没有扩展/前置的IClass,则没有解释步骤4是否在fido_instance
的单例类上执行。我不得不假设,因为整个方法查找会短路。
如果我猜测一组可以解释ruby的方法查找行为的步骤,它可能看起来像:
fido_instance.class
方法。 (显然,ruby不会使用自己的#class方法来进行方法查找,但它传达了流程的逻辑)fido_instance.class.superclass
方法。继续添加.superclass
并检查方法,直到没有剩余超类。 (同样,ruby不会使用自己的#superclass方法)我还记得读过,如果接收对象是一个类,那么会有一个单独的方法查找过程,但我无法回想起去哪里。
那么,正确,详细的解释是什么并不涉及了解C?
答案 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祖先中定义的。
要了解该祖先树是什么(而不是每次都依赖于打印出来),您只需要知道如何通过子类extend
,include
,{{ 1}}等。
prepend
在类include
中添加模块,将其添加到祖先链中,位于C
之后,然后再进行其他操作。C
在类prepend
中添加模块会将该模块添加到祖先链中,包括所有C
和任何当前前置的模块。C
将def x.method1
添加到method1
。同样,x.singleton_class
将x.extend(M)
添加到M
的祖先(但不添加到x.singleton_class
中)。请注意,后者正是x.class
和Bark
发生的情况,但同样可以应用于任何对象。从上面的列表中删除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
一样。