我正在努力全面了解Ruby如何定位方法/符号,但是当涉及多个级别时,尤其是全局/文件范围时,我正在苦苦挣扎。
在类上显式调用方法时,会在顺序上有很多插图,其中包含它们所包含的类和模块(因此在每种情况下都是super
调用的内容)。但是当没有明确地调用方法时,例如一个简单的func args
而不是self.func args
什么是搜索顺序?
为什么在下面的示例中,调用func
的成员方法在全局之前找到成员方法,但func2
找到没有调用method_missing
的全局?
当全局变量是模块/类/类型时,为什么根本找不到具有相同名称的成员?
在方法中遇到“func
,func()
,func arg
”等时,是否有任何官方文档确切说明该语言的作用?有很多第三方博客,但他们只是真正谈论了include Module
和class Type < BaseType
的单个实例。
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
答案 0 :(得分:4)
Ruby的方法查找算法实际上真的简单:
class
指针superclass
指针,然后重复那就是它。
如果算法到了没有更多超类的地步,但它仍然没有找到该方法,它将再次重新启动整个过程,并以method_missing
作为消息和名称原始消息的前缀是参数。但那就是它。这就是整个算法。它非常小而且非常简单, 非常小而且非常简单,因为方法查找是面向对象语言中最常执行的操作。
注意:我完全忽略了Module#prepend
/ Module#prepend_features
,因为我对它的运作方式还不太了解。我只知道它做了什么,这对我来说已经足够好了。
另请注意:我忽略了性能优化,例如在多态内联缓存中缓存方法查找的结果。
好的,但这就是诀窍:那些class
和superclass
指针到底指向哪里?好吧,他们不指向Object#class
和Class#superclass
方法返回的内容。所以,让我们退一步。
每个对象都有一个class
指针,指向对象的类。每个类都有一个指向其超类的superclass
指针。
让我们开始一个运行的例子:
class Foo; end
现在,我们有一个班级Foo
,其superclass
指针指向Object
。
foo = Foo.new
我们的对象foo
&#39; class
指针指向Foo
。
def foo.bar; end
现在事情开始变得有趣了。我们创建了一个单例方法。嗯,实际上,没有单例方法,它实际上只是单例类中的常规方法。那么,这是如何工作的?好吧,现在class
指针指向foo
的单例类,而foo
的单例类superclass
指针指向{ {1}}!换句话说,单例类被插入Foo
和它的&#34;真实&#34;之间。班foo
。
但是,当我们询问Foo
关于其课程时,它仍会回复foo
:
Foo
foo.class #=> Foo
方法知道单例类,只需在Object#class
指针后跳过它们,直到它找到&#34; normal&#34; class,并返回。
下一个并发症:
superclass
这里发生了什么? Ruby创建了一个 new 类(让我们称之为module Bar; end
class Foo
include Bar
end
),称为 include class 。该类的方法表指针,类变量表指针和常量表指针指向Barʹ
的方法表,类变量表和常量表。然后,Ruby使Bar
的{{1}}指针指向Barʹ
的当前超类,然后生成superclass
的超类指针指向Foo
。换句话说,包含一个模块会创建一个新类,该类作为包含模块的类的超类插入。
这里有一个小问题:你也可以Foo
模块进入模块。这是如何运作的?好吧,Ruby只是跟踪模块中包含的模块。然后,当模块被包含在一个类中时,它将递归地为每个包含的模块重复上述步骤。
您需要了解Ruby方法查找的所有内容:
现在让我们来看看你的一些问题:
在类上显式调用方法时,会在顺序上有很多插图,其中包含它们所包含的类和模块(因此在每种情况下都是
Barʹ
调用的内容)。但是当没有明确地调用方法时,例如一个简单的include
而不是super
什么是搜索顺序?
同样的。 func args
是隐式接收器,如果您没有指定接收器,则接收器为self.func args
。括号是可选的。换句话说:
self
与
完全相同self
为什么在我的示例中,调用
func args
的成员方法在全局之前找到成员方法,但是self.func(args)
找到没有调用func
的全局?
没有&#34;全球方法&#34;在Ruby中。也没有&#34;成员方法&#34;。每个方法都是一个实例方法。期。没有全局,静态,类,单例,成员方法,过程,函数或子例程。
在顶级定义的方法成为类func2
的{{1}}实例方法。 method_missing
继承自private
。运行我上面概述的步骤,您将确切地知道发生了什么:
Object
的类指针:Test
Object
是否有一个名为x
的方法:是的,请调用它。现在再次:
Test
的类指针:Test
func
是否有一个名为x
的方法:否!Test
的超类指针:Test
func2
是否有一个名为Test
的方法:是的,请调用它。当全局变为模块/类/类型时,为什么根本找不到具有相同名称的成员?
同样,这里没有全球,这里没有成员。这也与模块或类没有任何关系。而且Ruby没有(静态)类型。
Object
是对常量的引用。如果要调用具有相同名称的方法,则必须确保Ruby可以告诉它它是一种方法。 仅方法可以有两件事:接收器和参数。所以,你可以添加一个接收器:
Object
或参数:
func2
现在Ruby知道你的意思是方法Math
而不是常数self.Math
。
顺便说一句,这同样适用于局部变量。而且要制定者。如果你想调用一个setter而不是分配一个局部变量,你需要说
Math()