1. 程式人生 > >C#使用Emit構造攔截器動態代理類

C#使用Emit構造攔截器動態代理類

too don ati 記錄 type sem let module mic

在AOP編程概念介紹中,常見的示例為攔截對象,並在對象的某方法執行前和執行後分別記錄日誌。

而最常用的攔截方式是使用動態代理類,用其封裝一個日誌攔截器,當方法被執行時進行日誌記錄。

日誌攔截器類

技術分享圖片
 1 public class Interceptor
 2 {
 3   public object Invoke(object @object, string @method, object[] parameters)
 4   {
 5     Console.WriteLine(
 6       string.Format("Interceptor does something before invoke [{0}]...", @method));
 7 
 8     var retObj = @object.GetType().GetMethod(@method).Invoke(@object, parameters);
 9 
10     Console.WriteLine(
11       string.Format("Interceptor does something after invoke [{0}]...", @method));
12 
13     return retObj;
14   }
15 }
技術分享圖片

被攔截對象類

假設我們有一個Command類,包含一個方法Execute用於執行一些工作。

技術分享圖片
1 public class Command
2 {
3   public virtual void Execute()
4   {
5     Console.WriteLine("Command executing...");
6     Console.WriteLine("Hello Kitty!");
7     Console.WriteLine("Command executed.");
8   }
9 }
技術分享圖片

我們需要在Execute方法執行前和執行後分別記錄日誌。

動態代理類

技術分享圖片
  1 public class Proxy
  2 {
  3   public static T Of<T>() where T : class, new()
  4   {
  5     string nameOfAssembly = typeof(T).Name + "ProxyAssembly";
  6     string nameOfModule = typeof(T).Name + "ProxyModule";
  7     string nameOfType = typeof(T).Name + "Proxy";
  8 
  9     var assemblyName = new AssemblyName(nameOfAssembly);
 10     var assembly = AppDomain.CurrentDomain
 11       .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
 12     var moduleBuilder = assembly.DefineDynamicModule(nameOfModule);
 13 
 14     var typeBuilder = moduleBuilder.DefineType(
 15       nameOfType, TypeAttributes.Public, typeof(T));
 16 
 17     InjectInterceptor<T>(typeBuilder);
 18 
 19     var t = typeBuilder.CreateType();
 20 
 21     return Activator.CreateInstance(t) as T;
 22   }
 23 
 24   private static void InjectInterceptor<T>(TypeBuilder typeBuilder)
 25   {
 26     // ---- define fields ----
 27 
 28     var fieldInterceptor = typeBuilder.DefineField(
 29       "_interceptor", typeof(Interceptor), FieldAttributes.Private);
 30 
 31     // ---- define costructors ----
 32 
 33     var constructorBuilder = typeBuilder.DefineConstructor(
 34       MethodAttributes.Public, CallingConventions.Standard, null);
 35     var ilOfCtor = constructorBuilder.GetILGenerator();
 36 
 37     ilOfCtor.Emit(OpCodes.Ldarg_0);
 38     ilOfCtor.Emit(OpCodes.Newobj, typeof(Interceptor).GetConstructor(new Type[0]));
 39     ilOfCtor.Emit(OpCodes.Stfld, fieldInterceptor);
 40     ilOfCtor.Emit(OpCodes.Ret);
 41 
 42     // ---- define methods ----
 43 
 44     var methodsOfType = typeof(T).GetMethods(BindingFlags.Public | BindingFlags.Instance);
 45 
 46     for (var i = 0; i < methodsOfType.Length; i++)
 47     {
 48       var method = methodsOfType[i];
 49       var methodParameterTypes =
 50         method.GetParameters().Select(p => p.ParameterType).ToArray();
 51 
 52       var methodBuilder = typeBuilder.DefineMethod(
 53         method.Name,
 54         MethodAttributes.Public | MethodAttributes.Virtual,
 55         CallingConventions.Standard,
 56         method.ReturnType,
 57         methodParameterTypes);
 58 
 59       var ilOfMethod = methodBuilder.GetILGenerator();
 60       ilOfMethod.Emit(OpCodes.Ldarg_0);
 61       ilOfMethod.Emit(OpCodes.Ldfld, fieldInterceptor);
 62 
 63       // create instance of T
 64       ilOfMethod.Emit(OpCodes.Newobj, typeof(T).GetConstructor(new Type[0]));
 65       ilOfMethod.Emit(OpCodes.Ldstr, method.Name);
 66 
 67       // build the method parameters
 68       if (methodParameterTypes == null)
 69       {
 70         ilOfMethod.Emit(OpCodes.Ldnull);
 71       }
 72       else
 73       {
 74         var parameters = ilOfMethod.DeclareLocal(typeof(object[]));
 75         ilOfMethod.Emit(OpCodes.Ldc_I4, methodParameterTypes.Length);
 76         ilOfMethod.Emit(OpCodes.Newarr, typeof(object));
 77         ilOfMethod.Emit(OpCodes.Stloc, parameters);
 78 
 79         for (var j = 0; j < methodParameterTypes.Length; j++)
 80         {
 81           ilOfMethod.Emit(OpCodes.Ldloc, parameters);
 82           ilOfMethod.Emit(OpCodes.Ldc_I4, j);
 83           ilOfMethod.Emit(OpCodes.Ldarg, j + 1);
 84           ilOfMethod.Emit(OpCodes.Stelem_Ref);
 85         }
 86         ilOfMethod.Emit(OpCodes.Ldloc, parameters);
 87       }
 88 
 89       // call Invoke() method of Interceptor
 90       ilOfMethod.Emit(OpCodes.Callvirt, typeof(Interceptor).GetMethod("Invoke"));
 91 
 92       // pop the stack if return void
 93       if (method.ReturnType == typeof(void))
 94       {
 95         ilOfMethod.Emit(OpCodes.Pop);
 96       }
 97 
 98       // complete
 99       ilOfMethod.Emit(OpCodes.Ret);
100     }
101   }
102 }
技術分享圖片

使用動態代理類

技術分享圖片
 1 class Program
 2 {
 3   static void Main(string[] args)
 4   {
 5     var command = Proxy.Of<Command>();
 6     command.Execute();
 7 
 8     Console.WriteLine("Hi, Dennis, great, we got the interceptor works.");
 9     Console.ReadLine();
10   }
11 }
技術分享圖片

運行結果

技術分享圖片

完整代碼

技術分享圖片 View Code

下載完整代碼

C#使用Emit構造攔截器動態代理類