马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
归根到底,net网页编程跨平台可以,但是要重新编写代码,否则还分什么J2EE/J2SE/J2ME呢!Ruby2.0为模块增加了一个Module#prepend办法,依据API文档的形貌,它以相反的按次对参数一一挪用prepend_features办法。和模块包括相似,会把一个模块的先人链拔出到另外一个模块的先人链中,但跟模块包括把先人链插到该模块以后纷歧样,它会把先人链插到该模块之前。我喜好把这个特征叫做模块前置。
先举个例子来讲明模块前置的感化,并和模块包括作了对照。在这个例子中,类C前置了模块A和B;类D包括了模块A和B。
- moduleAdeffoo;AendendmoduleBdeffoo;BendendclassCprependA,B#Prependingisdonebythislinedeffoo;CendendclassDincludeA,Bdeffoo;DendendC.ancestors#=>[A,B,C,Object,Kernel,BasicObject]D.ancestors#=>[D,A,B,Object,Kernel,BasicObject]C.new.foo#=>AD.new.foo#=>D
复制代码 第10行,我们在C中前置了模块A和B,如许A和B就拔出到C的先人链中了。从21和22行中的正文中,我们能够看到,在C的先人链中,A和B位于C之前。而在D的先人链中,A和B位于D以后。
这就是为何第24行,C.new.foo的前往值是A,由于模块A前置于C,位于先人链的最后面,办法查找会优先找到A中的foo办法。
我们看到了模块前置的壮大的特征,但疑问也随之而来。最不言而喻的成绩是,C.ancestors为何不是从类C入手下手。要解开这个疑问,起首应当弄分明prepend办法都做了哪些事情。我们跟到源代码中往一探求竟,上面是prepend的默许完成Module#prepend对应的源代码:
- staticVALUErb_mod_prepend(intargc,VALUE*argv,VALUEmodule){inti;IDid_prepend_features,id_prepended;CONST_ID(id_prepend_features,"prepend_features");CONST_ID(id_prepended,"prepended");for(i=0;i<argc;i++)Check_Type(argv[i],T_MODULE);while(argc--){rb_funcall(argv[argc],id_prepend_features,1,module);rb_funcall(argv[argc],id_prepended,1,module);}returnmodule;}
复制代码 能够看到,它的举动和Module#include办法几近一样,只不外回调的办法纷歧样。这里,它回调了参数模块的prepend_features办法和prepended办法。一样,Module#prepend_features才是真正干活的中央,以是跟出来看看。
- staticVALUErb_mod_prepend_features(VALUEmodule,VALUEprepend){switch(TYPE(prepend)){caseT_CLASS:caseT_MODULE:break;default:Check_Type(prepend,T_CLASS);break;}rb_prepend_module(prepend,module);returnmodule;}
复制代码 它做了一些范例方面的反省,然后把事情交给了rb_prepend_module函数,我们看看rb_prepend_module函数做了甚么。
- [/code]
- [code]voidrb_prepend_module(VALUEklass,VALUEmodule){voidrb_vm_check_redefinition_by_prepend(VALUEklass);VALUEorigin;intchanged=0;rb_frozen_class_p(klass);if(!OBJ_UNTRUSTED(klass)){rb_secure(4);}Check_Type(module,T_MODULE);OBJ_INFECT(klass,module);origin=RCLASS_ORIGIN(klass);if(origin==klass){origin=class_alloc(T_ICLASS,klass);RCLASS_SUPER(origin)=RCLASS_SUPER(klass);RCLASS_SUPER(klass)=origin;RCLASS_ORIGIN(klass)=origin;RCLASS_M_TBL(origin)=RCLASS_M_TBL(klass);RCLASS_M_TBL(klass)=st_init_numtable();st_foreach(RCLASS_M_TBL(origin),move_refined_method,(st_data_t)RCLASS_M_TBL(klass));}changed=include_modules_at(klass,klass,module);if(changed<0)rb_raise(rb_eArgError,"cyclicprependdetected");if(changed){rb_clear_cache();rb_vm_check_redefinition_by_prepend(klass);}}
复制代码 这个函数做了一些事情,我们来剖析一下。前16行都是在做一些范例反省等事情,我们跳过。从第17行入手下手剖析。
起首,宏RCLASS_ORIGIN猎取klass的origin成员,而且把它和klass对照。我们不晓得origin字段有甚么感化,我们先假定测试前提为真,即klass的origin成员指向本身。我们来剖析一下if语句中的逻辑:
- 19举动klass创立了一个新的包括类,我们把它称为原始类;
- 20~21行把新创立的包括类拔出到klass和klass的父类两头;
- 22即将klass的origin成员指向了新类;
- 接上去,23~24行把klass的办法表转移到新类中,并清空klass的办法表;
- 最初,25行又把klass本来的办法表中的Refined办法移了返来。
剖析完if语句,我们持续行进,离开第28行。等等,你仿佛看到了熟习的工具。没错,那就是include_modules_at办法。在前一篇文章中,我们会商了这个函数,它用来包括某个模块。你几乎不敢信任本人的眼睛,明显是在前置模块,怎样俄然又酿成包括模块了?
是的,没错,它就是在包括模块。被包括的模块的先人链拔出到了klass和klass的原始类之间。因为klass外部的办法表已转移到下游的原始类中,以是拔出的地位恰好符合。Ruby经由过程这类变更,奇妙地将前置模块转化为包括模块,太棒了。
上面这个图形貌了文章开首的谁人例子中,类C中prependA,B语句实行前后的形态:
- +-----++--------+Before:|C|----->|Object|+-----++--------++---------------klass----------------+||v|+-----++-----++-----++-----++--------+After:|C|----->|A|----->|B|----->|C|+---->|Object|+-----++-----++-----++-----++--------+|^||+---------------origin---------------+
复制代码 正如之前剖析的那样,C就是谁人新创立的包括类,它是C的原始类。但假如是如许的话,仍是没法注释之前的疑问:为何C.ancestors不是从类C入手下手?要弄分明这个成绩,我们来看看C.ancestors是怎样事情的。
我们找到了Module#ancestors的源代码,它看起来对照复杂:
- VALUErb_mod_ancestors(VALUEmod){VALUEp,ary=rb_ary_new();for(p=mod;p;p=RCLASS_SUPER(p)){if(FL_TEST(p,FL_SINGLETON))continue;if(BUILTIN_TYPE(p)==T_ICLASS){rb_ary_push(ary,RBASIC(p)->klass);}elseif(p==RCLASS_ORIGIN(p)){rb_ary_push(ary,p);}}returnary;}
复制代码 该函数起首创立了一个数组,然后对遍历模块的先人链,对每一个先人,假如是包括类大概原始类指向本身,就放在前往的数组内里。别的,它还会跳过单例类。
至此,之前的疑问也失掉懂得释,C.ancestors并没有把C本身包括出来,由于它既不是包括类,也不是origin指向本身的类。而C.ancestors前往的数组中的C实际上是C的原始类,同时也是C的包括类,以是它才有C这个名字。
c语言的编译器,几乎是所有新平台都有的。因此从这点上看,c语言的程序,比其他任何语言更加容易跨平台。 |