Project1

标题: 在父类和子类中同时alias同名方法竟然会出错,啥原因 [打印本页]

作者: 六祈    时间: 2010-7-22 15:07
标题: 在父类和子类中同时alias同名方法竟然会出错,啥原因
本帖最后由 六祈 于 2010-7-22 17:53 编辑
  1. class Window_Selectable < Window_Base
  2.   alias old_initialize initialize
  3.   def initialize(x, y, width, height)
  4.     old_initialize(x, y, width, height)
  5.     @last_index = 0
  6.     @disable_items = []
  7.   end
  8.   
  9.   alias old_update_cursor_rect update_cursor_rect
  10.   def update_cursor_rect
  11.     case self.method(:draw_item).arity
  12.     when 2
  13.       draw_item(@last_index,@disable_items.include?(@last_index) ? disabled_color : normal_color)
  14.       draw_item(@index,system_color)
  15.     else
  16.     old_update_cursor_rect
  17.     end
  18.     @last_index = @index
  19.   end
  20. end

  21. class Window_PartyCommand < Window_Selectable
  22.   alias old_update_cursor_rect update_cursor_rect
  23.   def update_cursor_rect
  24.     super()
  25.   end
  26.   alias old_initialize initialize
  27.   def initialize
  28.     old_initialize()
  29.     @disable_items.push 1 unless $game_temp.battle_can_escape
  30.   end
  31. end
复制代码
4行出错,4个实参赋给0个形参

27行的alias后的old_initialize改成别的名字就没问题- -

求真理
作者: IamI    时间: 2010-7-22 15:17
我举个例子好了。
A<B<C,A当中有个方法叫a
那么在B类中定义 alias b a
那么C当中还有没有b方法呢?当然有啊。
完毕= =
作者: 紫苏    时间: 2010-7-22 15:22
本帖最后由 紫苏 于 2010-7-22 17:05 编辑

4  行 alias 的是 Window_Selectable 的原始 initialize 方法,接着重定义 Window_Selectable#initialize,通过别名 old_initialize 调用原始 initialize 方法;
如果没有在别处定义 Window_PartyCommand#initialize 的话,27 行 alias 的是父类方法 Window_Selectable#initialize,也就是上面刚被重定义的那个 initialize。之后在 Window_PartyCommand 中定义 initialize 方法调用 old_initialize,由于 old_initialize 是顶部被重定义的 Window_Selectable#initialize,并且需要 4 个参数,而 Window_PartyCommand#initialize 调用 old_initialize 时却只传递了 0 个参数,所以会有这个异常。

如果参数列表刚好吻合,就不会有这个异常,但却会产生一个没有出口的递归调用(因为 old_initialize 是 Window_Selectable#initialize 方法本身),最终导致 “Stack level too deep”……

作者: IamI    时间: 2010-7-22 15:26
4  行 alias 的是 Window_Selectable 的原始 initialize 方法,接着重定义 Window_Selectable#initialize, ...
紫苏 发表于 2010-7-22 15:22


于是内建类的悲剧诞生了?那么上次那个反F12是什么原理……(故意歪楼)


作者: 六祈    时间: 2010-7-22 15:33
回复 紫苏 的帖子
嗯,的确是这样子,不过我还有几个疑惑不太明白

1.为什么打开window_partycommand类的alias会去操作父类呢
2.为什么window_partycommand类中的old_initialize会去调用父类的old_initialize而父类的同名方法却去调用子类的?表示很晕菜啊
作者: zhangbanxian    时间: 2010-7-22 15:41
alias old_initialize initialize相当于定义了父类的old_initialize
父类有了old_initialize就相当于子类有了old_initialize
既然old_initialize为已存在的方法名,自然就无法用来作别的方法的别名了
作者: 紫苏    时间: 2010-7-22 15:41
回复 IamI 的帖子

防 F12 的方式和 C/C++ 避免重复包含头文件的方法基本一致,就是给脚本加个“包含防护罩”:
if(!$__script__built__)
$__script__built__ = 0

# ... 脚本内容 ...

end

话说我咋记得内置的 RGSS 类反而不会有这个问题来着……
作者: 六祈    时间: 2010-7-22 15:47
回复 紫苏 的帖子
如果没有在别处定义 Window_PartyCommand#initialize 的话,27 行 alias 的是父类方法 Window_Selectable#initialize,也就是上面刚被重定义的那个 initialize。

可是如果27行的别名取做别的就ok了,window_partycommand#initialize也正常(战斗和逃跑指令),愚还是觉得很诡异。。。   


   
作者: 紫苏    时间: 2010-7-22 16:09
回复 六祈 的帖子

27 行取别的名字后,父类的 initialize 里调用的 old_initialize 就仍然是 Window_Selectable 的原始 initialize 方法,而不是子类的 Initialize 了;只有当子类仍然给 initialize 取 old_initialize 这个别名时,父类才会又回去调用子类的 initialize
作者: 六祈    时间: 2010-7-22 16:09
回复 紫苏 的帖子

27行:alias old_initialize initialize
old_initialize如果和父类的同名,那么结果是出错。那么是因为initialize是父类的initialize(因为调用了父类中的old_initialize)?还是者说这个old_initialize仍然去调用父类的这种方法,然后父类的old_initialize又去调用子类的?
而如果改old_initialize的名字的话,那么结果正常。initialize就是子类的了(因为是战斗逃跑菜单,initialize中含有这些信息,如果调用了父类肯定没有这些字了)
   
作者: 六祈    时间: 2010-7-22 16:17
本帖最后由 六祈 于 2010-7-22 16:56 编辑

回复 六祈 的帖子
真理貌似如下:



一旦一个类中alias x y了
那么遇到任何调用x的方法,都会去寻找y
于是27行后面调用的old_xxx去调用原来的xxx,而原来的xxx含有super去调用父类中的xxx,父类也有alias,xxx去调用old_xxx,然后悲剧发生了,子类在自己的环境中寻找xxx,于是参数异常,如果不异常,则死循环(父子类一直互相调用)

   
谢谢紫苏和IamI两位大人的答疑解惑~bow~
   
作者: DeathKing    时间: 2010-7-22 16:34
本帖最后由 DeathKing 于 2010-7-22 16:38 编辑
  1. irb(main):001:0> class A;def a; puts 1;end; alias :c :a;end
  2. => nil
  3. irb(main):002:0> class B<A
  4. irb(main):003:1> def b
  5. irb(main):004:2> puts 2
  6. irb(main):005:2> end
  7. irb(main):006:1> puts respond_to? :c
  8. irb(main):007:1> puts respond_to? :a
  9. irb(main):008:1> end
  10. false
  11. false
  12. => nil
  13. irb(main):009:0> B.superclass
  14. => A
复制代码
不知道是不是因为Ruby 1.9.1的缘故 - -

ri Module#respond_to?

获取帮助(respond_to? 属于元编程(反射机制)的范畴,获取是否可以使用从父类继承的方法。)

我唯一可以说的是,可能Ruby 1.9.1对alias做了手脚,alias方法后,子类就不能使用从父类继承的方法了?但是实例却可以调用?
作者: 紫苏    时间: 2010-7-22 17:08
发现我之前说错了一句话,在原帖编辑了一下,红色部分是修改过的……||||
作者: DeathKing    时间: 2010-7-22 17:33
本帖最后由 DeathKing 于 2010-7-22 17:42 编辑

回复 DeathKing 的帖子
  1. class A
  2.   def a
  3.     puts 1
  4.   end
  5.   alias :b :a
  6.   undef :a
  7. end

  8. A.new.b
  9. #=> 1
复制代码
才交完水电气回来看到
   

[line]1[/line]

一旦一个类中alias x y了
那么遇到任何调用x的方法,都会去寻找y

如果真这样的话
假设y方法已定义
alias x y
def y
x
end
这样就会死循环了。
作者: 六祈    时间: 2010-7-22 17:36
猜猜看这个结果
  1. class A
  2. def a
  3. b
  4. end
  5. def b
  6. puts 2
  7. end
  8. end
  9. class B < A
  10. def c
  11. puts 3
  12. end
  13. alias b c
  14. end

  15. puts B.new.a
复制代码

作者: david50407    时间: 2010-7-23 08:55
是3嗎?




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1