基於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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章