关于全局/范围的Ruby方法查找
我试图全面理解Ruby是如何定位方法/符号的,但当它涉及多个级别时,尤其是全局/文件范围时,我很难理解 当在类上显式调用方法时,有很多关于类及其包含的模块的搜索顺序的说明(因此在每种情况下,关于全局/范围的Ruby方法查找,ruby,methods,Ruby,Methods,我试图全面理解Ruby是如何定位方法/符号的,但当它涉及多个级别时,尤其是全局/文件范围时,我很难理解 当在类上显式调用方法时,有很多关于类及其包含的模块的搜索顺序的说明(因此在每种情况下,super调用什么)。但是当不显式调用方法时,例如普通的func-args而不是self.func-args时,搜索顺序是什么 为什么在我下面的示例中,调用func的成员方法在全局方法之前找到成员方法,而func2在没有调用method\u的情况下找到全局方法? 当全局变量改为模块/类/类型时,为什么找不到具
super
调用什么)。但是当不显式调用方法时,例如普通的func-args
而不是self.func-args
时,搜索顺序是什么
为什么在我下面的示例中,调用func
的成员方法在全局方法之前找到成员方法,而func2
在没有调用method\u的情况下找到全局方法?
当全局变量改为模块/类/类型时,为什么找不到具有相同名称的成员
当语言在方法中遇到“func
,func()
,func arg
”等时,是否有任何官方文档说明它的具体操作?有很多第三方博客,但他们只讨论了带有include Module
和class-Type
的单个实例
def func; "global func" end
def func2; "global func 2" end
class Test
def x; func end
def y; func2 end
def z; Math end
def w
func = "local_var"
[func(), func]
end
def func(arg=nil); "method func" end
def func=(x); puts "assign func=" end
def Math; "method Math" end
def method_missing(sym, *args, &block)
puts "method_missing #{sym}"
super(sym, *args, &block)
end
end
x = Test.new
puts x.x.inspect # "method func", member overrides global
puts x.y.inspect # "global func 2", "method_missing" was not called
puts x.z.inspect # "Math" module, member did not override global
puts x.w.inspect # ["method_func", "local_var"], local variables are always considered before anything else
Ruby的方法查找算法实际上非常简单:
- 检索接收器的
类指针
- 如果该方法存在,则调用它
- 否则检索
超类
指针,然后重复
就这样
如果算法到了不再有超类的地步,但仍然没有找到方法,它将重新启动整个过程,并将method\u missing
作为消息,原始消息的名称放在参数前面。但就是这样。这就是整个算法。它非常小且非常简单,而且必须非常小且非常简单,因为方法查找是面向对象语言中最常执行的操作
注意:我完全忽略了/因为我对它的工作原理了解不够。我只知道它的作用,这对我来说已经足够了
还要注意:我忽略了性能优化,比如在多态内联缓存中缓存方法查找的结果
好的,但这里有个窍门:那些类和超类的指针到底指向哪里?好吧,它们并不指向和方法返回的内容。所以,让我们退一步
每个对象都有一个指向对象类的class
指针。每个类都有一个指向其超类的指针
让我们开始一个运行示例:
class Foo; end
现在,我们有了类Foo
,它的超类
指针指向对象
foo = Foo.new
我们的对象foo
的class
指针指向foo
def foo.bar; end
现在事情开始变得有趣起来。我们创建了一个单例方法。实际上,没有单例方法,它只是单例类中的一个普通方法。那么,这是如何工作的呢?现在,class
指针指向foo
的单例类和foo
的单例类的超类
指针指向foo
!换句话说,singleton类被插入到foo
和它的“real”类foo
之间
def foo.bar; end
但是,当我们询问foo
其类时,它仍然会回答foo
:
foo.class #=> Foo
Object#class
方法知道单例类,并简单地跳过它们,跟随superclass
指针,直到找到一个“普通”类,并返回该类
下一个并发症:
module Bar; end
class Foo
include Bar
end
这里发生了什么?Ruby创建了一个名为include类的新类(我们称之为Barʹ
)。此类的方法表指针、类变量表指针和常量表指针指向Bar
的方法表、类变量表和常量表。然后,Ruby使Barʹ
的超类
指针指向Foo
的当前超类,然后使Foo
的超类指针指向Barʹ
。换句话说,包含模块将创建一个新类,该类将作为包含模块的类的超类插入
这里有一点复杂:您还可以将
模块包含到模块中。这是怎么回事?好的,Ruby只是跟踪包含在模块中的模块。然后,当模块被包含到一个类中时,它将对每个包含的模块递归地重复上述步骤
关于Ruby方法查找,您只需要了解这些:
- 找班
- 跟随超类
- 单例类插入对象上方
- 包含类插入上述类
现在让我们看看你的一些问题:
当在类上显式调用方法时,有很多关于类及其包含的模块的搜索顺序的说明(因此在每种情况下,super
调用什么)。但是当不显式调用方法时,例如普通的func-args
而不是self.func-args
时,搜索顺序是什么
同样的self
是隐式接收器,如果未指定接收器,则接收器为self
。和括号是可选的。换言之:
func args
完全一样
为什么在我下面的示例中,调用func
的成员方法在全局方法之前找到成员方法,而func2
在没有调用method\u的情况下找到全局方法
Ruby中没有“全局方法”这样的东西。也没有所谓的“成员方法”。每个方法都是一个实例方法。时期没有全局、静态、类,
Math
self.Math
Math()
self.func = 'setter method'