注册
 找回密码
 注册
江西广告网
查看: 233|回复: 0
打印 上一主题 下一主题

教你通过 Emit 实现动态生成一个类

[复制链接]

该用户从未签到

1
跳转到指定楼层
发表于 2008-12-24 11:59:02 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册

x
  动态生成一个类对于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;      ///   /// 构造函数   ///   /// 被实现或者继承的类型   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  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的类生成,倒是不错    <
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表