• 基于CodeGenerator的Emit代码生成辅助类源码及演示


    本文介绍一组NBearV4中的基于Emit动态生成代码的辅助类,部分概念在本人的blog之前的文章中或多或少都有介绍,这里包含最新的更新及演示、测试。主要是两个类:CodeGenerator和DynamicMethodFactory。前者提供了一种经过封装的,简化Emit方法(包括Emit DynamicMethod,Constructor,Method,get、set Method of Property)的方案;后者基于前者,实现了一种访问指定类(可以是第三方程序集的internal类)的方法或成员变量,实例化第三方程序集中的internal类型,高性能的以非泛型语法访问泛型方法的机制(通过DynamicMethod和Delegate实现)。

    下载源码:NBear.Common.zip

    介绍

    CodeGenerator

    该类很多地方参照了.NET 3.0的System.Runtime.Serialization.dll中的同名internal类,他封装了Emit中的各种Emit层面的常用操作逻辑,包括Ld各种value、成员变量,if-else,case switch,loop等分支控制等,扩展的版本使用DesignByContract对所有的输入参数进行了检查,并扩展了对Emit Constructor,Method,get、set Method of Property的支持。

    关于Emit DynamicMethod的示例,大家可以参见稍后介绍的DynamicMethodFactory类,这里先给出一个使用该类Emit一个类,并实现一个接口的示例代码,该示例代码为包含于源码的CodeGenerator.cs文件末尾的UnitTest代码:

     1    namespace CodeGeneratorUnitTest
     2    {
     3        public interface ITest
     4        {
     5            string Wow(string str);
     6        }

     7
     8        public class UnitTest
     9        {
    10            public static void TestEmitInterface()
    11            {
    12                AssemblyName assName = new AssemblyName("TestEmitInterface");
    13                AssemblyBuilder assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
    14                ModuleBuilder modBuilder = assBuilder.DefineDynamicModule(assBuilder.GetName().Name);
    15                TypeBuilder typeBuilder = modBuilder.DefineType("TestEmitInterface.TestImpl", TypeAttributes.Public);
    16                typeBuilder.AddInterfaceImplementation(typeof(ITest));
    17
    18                CodeGenerator ctor = new CodeGenerator(typeBuilder, "ctor", MethodAttributes.Public, CallingConventions.Standard, null, Type.EmptyTypes);
    19                ctor.Ldarg(0);
    20                ctor.Call(typeof(object).GetConstructor(Type.EmptyTypes));
    21                ctor.Ret();
    22
    23                MethodInfo mi = typeof(ITest).GetMethod("Wow");
    24
    25                CodeGenerator wow = new CodeGenerator(typeBuilder, mi.Name, mi.Attributes & (~MethodAttributes.Abstract) | MethodAttributes.Public, mi.CallingConvention, mi.ReturnType, new Type[] typeof(string) });
    26                wow.Ldarg(1);
    27                wow.Ret();
    28
    29                typeBuilder.DefineMethodOverride(wow.CurrentMethod, mi);
    30
    31                Type testImplType = typeBuilder.CreateType();
    32                ITest test = (ITest)Activator.CreateInstance(testImplType);
    33                Check.Assert(test.Wow("hello"== "hello");
    34            }

    35        }

    36    }

    以上代码Emit了一个TestImpl类,它实现了ITest接口,包含一个默认构造函数和一个Wow方法,注意,构造函数和方法都是通过CodeGenerator Emit的,这里的逻辑比较简单,但应该已经能看到相对于ilGen.Emit(OpCodes.XXX, YYY)这样的语法的简化,如果实现逻辑复杂,对整个Emit过程的简化就更明显。

    DynamicMethodFactory

    该类的主要功能包括:实例化第三方程序集中的internal类型(DynamicMethodFactory.CreateInstance()方法),为指定类型(可以是第三方程序集中的internal类型)的泛型或非泛型方法、属性、字段的读写生成非强类型的Delegate(通过DynamicMethod实现,不使用反射,性能接近直接访问)。

    下面先给出一个该类中为一个Method创建一个DynamicMethod,并返回其Delegate的示例,DynamicMethod是使用前面介绍的CodeGenerator实现的:

     1        protected static DynamicMethodProxyHandler DoGetMethodDelegate(
     2            Module targetModule,
     3            MethodInfo genericMethodInfo,
     4            params Type[] genericParameterTypes)
     5        {
     6            Check preconditions
    16
    17            //Create a dynamic method proxy delegate used to call the specified methodinfo
    18            CodeGenerator gen = new CodeGenerator(targetModule);
    19            gen.BeginMethod("dm" + Guid.NewGuid().ToString("N"), typeof(DynamicMethodProxyHandler));
    20            MethodInfo makeGenericMethodInfo = MakeMethodGeneric(genericMethodInfo, genericParameterTypes);
    21            gen.Ldarg(0);
    22            LoadParameters(gen, makeGenericMethodInfo.GetParameters(), false);
    23            gen.Call(makeGenericMethodInfo);
    24            CastValueToObject(gen, makeGenericMethodInfo.ReturnType);
    25
    26            return (DynamicMethodProxyHandler)gen.EndMethod();
    27        }

    LoadParameters和CastValueToObject的代码

    代码是不是相对比较简单呢(当然是相对于自己写所有的Emit来讲的),注意这里的LoadParameter方法的实现您可以发现,为方法生成调用Delegate是完美支持输入输出参数的。

    下面给出
    DynamicMethodFactory类的UnitTest代码,演示了对方法、字段和属性的生成Delegate和基于Delegate的读写,且包括对输入输出参数的使用:

      1    namespace DynamicMethodFactoryUnitTest
      2    {
      3        public class TestClass
      4        {
      5            public static void StaticReturnVoidMethod()
      6            {
      7            }

      8
      9            public static int StaticReturnIntMethod(string str, int i, ref int refInt, ref string refStr)
     10            {
     11                Check.Assert(str == "str");
     12                Check.Assert(i == 1);
     13                Check.Assert(refInt == 3);
     14                Check.Assert(refStr == "instr");
     15
     16                int ret = i + refInt;
     17                refInt = i + 1;
     18                refStr = "ref" + str;
     19
     20                Check.Assert(refInt == 2);
     21                Check.Assert(ret == 4);
     22                Check.Assert(refStr == "refstr");
     23
     24                return ret;
     25            }

     26
     27            public static int StaticIntField;
     28
     29            public static int StaticIntProperty
     30            {
     31                get
     32                {
     33                    return StaticIntField;
     34                }

     35                set
     36                {
     37                    StaticIntField = value;
     38                }

     39            }

     40
     41            public void NonStaticReturnVoidMethod()
     42            {
     43            }

     44
     45            public int NonStaticReturnIntMethod(string str, int i, out int outInt, out string outStr)
     46            {
     47                outInt = i + 1;
     48                Check.Assert(outInt == 2);
     49                outStr = "out" + str;
     50                Check.Assert(outStr == "outstr");
     51                return i + 2;
     52            }

     53
     54            public int NonStaticIntField;
     55
     56            public int NonStaticIntProperty
     57            {
     58                get
     59                {
     60                    return NonStaticIntField;
     61                }

     62                set
     63                {
     64                    NonStaticIntField = value;
     65                }

     66            }

     67        }

     68
     69        public class UnitTest
     70        {
     71            private static DynamicMethodFactory fac = new DynamicMethodFactory();
     72
     73            public static void TestStaticMethod()
     74            {
     75                StaticDynamicMethodProxyHandler handler = fac.GetStaticMethodDelegate(typeof(TestClass).GetMethod("StaticReturnVoidMethod"));
     76                handler(null);
     77
     78                object[] inputParams = new object[] "str"13"instr" };
     79                handler = fac.GetStaticMethodDelegate(typeof(TestClass).GetMethod("StaticReturnIntMethod"));
     80                object ret = handler(inputParams);
     81                Check.Assert(((int)inputParams[2]) == 2);
     82                Check.Assert(((string)inputParams[3]) == "refstr");
     83                Check.Assert(((int)ret) == 4);
     84            }

     85
     86            public static void TestStaticField()
     87            {
     88                TestClass.StaticIntField = -1;
     89                FieldInfo field = typeof(TestClass).GetField("StaticIntField"); ;
     90                StaticDynamicMethodProxyHandler handler = fac.GetStaticFieldSetDelegate(field);
     91                handler(new object[] 5 });
     92                Check.Assert(TestClass.StaticIntField == 5);
     93                handler = fac.GetStaticFieldGetDelegate(field);
     94                Check.Assert(((int)handler(null)) == 5);
     95            }

     96
     97            public static void TestStaticProperty()
     98            {
     99                TestClass.StaticIntField = -1;
    100                PropertyInfo property = typeof(TestClass).GetProperty("StaticIntProperty"); ;
    101                StaticDynamicMethodProxyHandler handler = fac.GetStaticMethodDelegate(property.GetSetMethod());
    102                handler(new object[] 5 });
    103                Check.Assert(TestClass.StaticIntProperty == 5);
    104                handler = fac.GetStaticMethodDelegate(property.GetGetMethod());
    105                Check.Assert(((int)handler(null)) == 5);
    106            }

    107
    108            public static void TestNonStaticMethod()
    109            {
    110                TestClass obj = new TestClass();
    111
    112                DynamicMethodProxyHandler handler = fac.GetMethodDelegate(typeof(TestClass).GetMethod("NonStaticReturnVoidMethod"));
    113                handler(obj, null);
    114
    115                object[] inputParams = new object[] "str"1nullnull };
    116                handler = fac.GetMethodDelegate(typeof(TestClass).GetMethod("NonStaticReturnIntMethod"));
    117                object ret = handler(obj, inputParams);
    118                Check.Assert(((int)inputParams[2]) == 2);
    119                Check.Assert(((string)inputParams[3]) == "outstr");
    120                Check.Assert(((int)ret) == 3);
    121            }

    122
    123            public static void TestNonStaticField()
    124            {
    125                TestClass obj = new TestClass();
    126                obj.NonStaticIntField = -1;
    127
    128                FieldInfo field = typeof(TestClass).GetField("NonStaticIntField"); ;
    129                DynamicMethodProxyHandler handler = fac.GetFieldSetDelegate(field);
    130                handler(obj, new object[] 5 });
    131                Check.Assert(obj.NonStaticIntField == 5);
    132                handler = fac.GetFieldGetDelegate(field);
    133                Check.Assert(((int)handler(obj, null)) == 5);
    134            }

    135
    136            public static void TestNonStaticProperty()
    137            {
    138                TestClass obj = new TestClass();
    139                obj.NonStaticIntField = -1;
    140
    141                PropertyInfo property = typeof(TestClass).GetProperty("NonStaticIntProperty"); ;
    142                DynamicMethodProxyHandler handler = fac.GetMethodDelegate(property.GetSetMethod());
    143                handler(obj, new object[] 5 });
    144                Check.Assert(obj.NonStaticIntField == 5);
    145                handler = fac.GetMethodDelegate(property.GetGetMethod());
    146                Check.Assert(((int)handler(obj, null)) == 5);
    147            }

    148        }

    149    }

    DynamicMethodFactory类还可以用于以非泛型方法的调用语法调用泛型方法,在之前的一篇文章介绍过,这里就不重复了,感兴趣的朋友可以参见:改进的以非泛型方式调用泛型方法”之基于DynamicMethod的实现

    有任何问题欢迎回复讨论。

    //The End

  • 相关阅读:
    学习ios键盘和textfield之间操作体会
    关于Cannot assign to 'self' outside of a method in the init family解决方法
    "this class is not key value coding-compliant for the key ..."问题的解决
    在编译oc中protocol时出现的错误
    关于oc中出现的typedef的用法/定义函数指针
    VC++、MFC、COM和ATL的区别
    leetcode : Spiral Matrix II
    leetcode : Length of Last Word [基本功]
    leetcode : triangle
    leetcode : Insert Interval
  • 原文地址:https://www.cnblogs.com/teddyma/p/919449.html
Copyright © 2020-2023  润新知