通过Emit实现动态类生成

动态生成一个类对于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 moveing
I´m eating







总结:
如果用于AOP的话,Emit可以动态生成一个装饰类,相比于基于Remoting架构的TP/RP的方法,效率可能要高些,而且还能拦截new操作符。缺点:对于非Virtual的方法,似乎无法拦截。
用于O/R Mapping的类生成,倒是不错

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值