Ruby China

Ruby 小试验,关于 Ruby 类的祖先链

kevin__liu · 2012年06月02日 · 最后由 zlx_star 回复于 2012年06月08日 · 4729 次阅读
module M; end
module M1; end
module M2; end
class MyClass
 include M
 include M1
end
class MySubClass < MyClass
 include M
 include M2
end
obj = MySubClass.new
puts obj.class.ancestors()
puts "\n\n\n"
puts BasicObject.superclass==nil
def my_ancestors(current_class)
 a = []
 a.push current_class
 sc = current_class.superclass
 until sc==nil
 a.push sc
 sc = sc.superclass
 end
 a
end
puts my_ancestors(obj.class)

运行结果如下: MySubClass M2 MyClass M1 M Object Kernel BasicObject

true MySubClass MyClass Object BasicObject

注意:模块相当于在导入模块的类与 superclass 之间加了一个匿名类。祖先链中如果两个类导入了相同的模块(M)的话,这个匿名类只是相当于加在了最祖先的类上层。 可能我解析的有错误,对这方面有研究的同学可以给个正解。

匿名 #1 2012年06月02日

把 module M 改下

module M
 def self.included other
 puts "p:#{other} include M"
 end
end

再运行你的代码结果是

p:MyClass include M p:MySubClass include M MySubClass M2 MyClass M1 M Object Kernel BasicObject

true MySubClass MyClass Object BasicObject 无需解释了吧

匿名 #2 2012年06月02日

好像还是有点问题,included 这个函数可能只是在 include 方法调用时回调。不过不管第二次是不是真正的混入了,但从语言逻辑上来说是认为混入了两次

一个类的单件类的超类是这个类的超类的单件类。也就是说单件类也是有继承关系的,相同的模块当然不能被继承两次了。。。。

#1 楼 @kevin__liu

首先,对于对楼主的探索精神,深表佩服!! 我其实也喜欢没事儿研究这个。

我记得是双飞燕上面写的很清楚,所有被混入的类,会被加入一个数组,再次混入,将被忽略。

以你的示例来说,在 MySubClass 内混入 M 的那个语句,完全被忽略的。

#5 楼 @zw963 我很佩服你,在社区里,也关注到你了,你比我更有探索精神。

这些语言逻辑可能要理论加实际才能记得住,我主要是学习过程中做点例子,加深一下印象,在这里贴出来,目的主要是想新手或者平常编程不太喜欢求其解的同学们一同进步。 也希望大牛们不要忘记自己是牛,向 @zw963 @jjym @messiahxu 学习,有时间就多回帖,让大家学好 Ruby 语言。

呵呵。共勉吧。比起社区很多人,我绝对是新新人类~ 哈哈

刚才回答你问题后,回来后,我又想到为什么不妨看看 Ruby 底层的操作码。

下面是操作码输出的一部分,前一块是父类 A 内混入模块 MyModule 的定义,后一块是子类 B 内混入同一模块的定义。

== disasm: <RubyVM::InstructionSequence:<class:A>@<compiled>>===========
0000 trace 2 ( 1)
0002 trace 1
0004 putnil 
0005 getinlinecache 12, <ic:0>
0008 getconstant :MyModule
0010 setinlinecache <ic:0>
0012 send :include, 1, nil, 8, <ic:1>
0018 trace 4
0020 leave 
== disasm: <RubyVM::InstructionSequence:<class:B>@<compiled>>===========
0000 trace 2 ( 1)
0002 trace 1
0004 putnil 
0005 getinlinecache 12, <ic:0>
0008 getconstant :MyModule
0010 setinlinecache <ic:0>
0012 send :include, 1, nil, 8, <ic:1>
0018 trace 4
0020 leave 

虽然我并不完全明白这些乱七八糟的伪码。不过可以很清楚的看出来,两次调用的参数是完全一样的。

send :include,1,nil,8,<ic:1>

这至少说明一点:这两次混入操作,都仅仅是方法调用而已,而且操作的是同样的位置的同样的对象。

我认为是每个类维护了一个祖先链的数组,而在实际执行 class MySubClass < MyClass 时: 首先从祖先哪里继承了祖先链,然后执行 include M include M2 时,根据祖先链中是否含有该模块决定是否加载。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
打算 60 岁还写代码,坚持住!

上海



共收到 8 条回复

AltStyle によって変換されたページ (->オリジナル) /