动态生成一个类对于aop,o/r mapping等技术非常有帮助。对于java来说,问题不大,而对于.net,则要麻烦些(主要麻烦在于实现代码的生成需要il),故猜测这可能也是在aop, o/r mapping方面,java走得略前的原因吧。
麻烦归麻烦,非不能也,动态生成一个简单的类还不至于太难。
假设有如下接口:
| interface ianimal { void move(); void eat(); } |
希望能创建一个类生成器typecreator,并能以以下方式使用:
| typecreator tc=new typecreator(typeof(ianimal)); type t = tc.build(); ianimal myanimal= (ianimal)activator.createinstance(t); myanimal.move(); myanimal.eat(); |
首先,发现system.reflection.emit.typebuilder似乎就是一个现成的类生成器。 不过typebuilder既没有实用的static方法,也不能在外部实例化。不过modulebuilder倒有一个definetype()方法,可以得到typebuilder;而modulebuilder和typerbuilder一个德行,不能直接创建,得从assemblybuilder的definedynamicmodule()方法得到。追根溯源,assemblybuilder得从appdomain的definedynamicassembly()的得来。最终好在appdomain提供了一个静态方法:appdomain.currentdomain. 这一连串并非没有道理,类型是依附于module的,而module依附于assembly,而assembly则被appdomain装载。所谓“皮之不存,毛将焉附”,为了创建type这个“毛”,得先把assembly,module这些“皮”依次构造出来:
| using system; using system.reflection; using system.reflection.emit; public class typecreator { private type targettype; /// <summary> /// 构造函数 /// </summary> /// <param name="targettype">被实现或者继承的类型</param> public typecreator(type targettype) { this.targettype = targettype; } public type build() { //获取当前appdomain appdomain currentappdomain = appdomain.currentdomain; //system.reflection.assemblyname 是用来表示一个assembly的完整名称的 assemblyname assyname = new assemblyname(); //为要创建的assembly定义一个名称(这里忽略版本号,culture等信息) assyname.name = "myassyfor_" + targettype.name; //获取assemblybuilder //assemblybuilderaccess有run,save,runandsave三个取值 assemblybuilder assybuilder = currentappdomain.definedynamicassembly(assyname,assemblybuilderaccess.run); //获取modulebuilder,提供string参数作为module名称,随便设一个 modulebuilder modbuilder = assybuilder.definedynamicmodule("mymodfor_"+targettype.name); //新类型的名称:随便定一个 string newtypename = "imp_"+targettype.name; //新类型的属性:要创建的是class,而非interface,abstract class等,而且是public的 typeattributes newtypeattribute = typeattributes.class | typeattributes.public; //声明要创建的新类型的父类型 type newtypeparent; //声明要创建的新类型要实现的接口 type[] newtypeinterfaces; //对于基类型是否为接口,作不同处理 if(targettype.isinterface) { newtypeparent = null; newtypeinterfaces = new type[]{targettype}; } else { newtypeparent = targettype; newtypeinterfaces = new type[0]; } //得到类型生成器 typebuilder typebuilder = modbuilder.definetype(newtypename,newtypeattribute,newtypeparent,newtypeinterfaces); //以下将为新类型声明方法:新类型应该override基类型的所以virtual方法 //得到基类型的所有方法 methodinfo[] targetmethods = targettype.getmethods(); //遍历各个方法,对于virtual的方法,获取其签名,作为新类型的方法 foreach(methodinfo targetmethod in targetmethods) { //只挑出virtual的方法 if(targetmethod.isvirtual) { //得到方法的各个参数的类型 parameterinfo[] paraminfo = targetmethod.getparameters(); type[] paramtype = new type[paraminfo.length]; for(int i=0;i<paraminfo.length;i++) paramtype[i] = paraminfo[i].parametertype; //传入方法签名,得到方法生成器 methodbuilder methodbuilder = typebuilder.definemethod(targetmethod.name,methodattributes.public| methodattributes.virtual,targetmethod.returntype,paramtype); //由于要生成的是具体类,所以方法的实现是必不可少的。而方法的实现是通过emit il代码来产生的 //得到il生成器 ilgenerator ilgen = methodbuilder.getilgenerator(); //以下三行相当于:{console.writeln("i'm "+ targetmethod.name +"ing");} ilgen.emit(opcodes.ldstr,"i'm "+ targetmethod.name +"ing"); ilgen.emit(opcodes.call,typeof(console).getmethod("writeline",new type[]{typeof(string)})); ilgen.emit(opcodes.ret); } } //真正创建,并返回 return(typebuilder.createtype()); } } |
好了,测试一下试试看:
| using system; public class tester { public static void main(string[] args) { typecreator tc=new typecreator(typeof(ianimal)); type t = tc.build(); ianimal animal= (ianimal)activator.createinstance(t); animal.move(); animal.eat(); console.read (); } } |
得到输出:i'm moveingi'm eating。
总结
如果用于aop的话,emit可以动态生成一个装饰类,相比于基于remoting架构的tp/rp的方法,效率可能要高些,而且还能拦截new操作符。缺点:对于非virtual的方法,似乎无法拦截。用于o/r mapping的类生成,倒是不错。
闽公网安备 35060202000074号