C#使用Emit构造拦截器动态代理类

时间:2023-03-08 21:54:57

在AOP编程概念介绍中,常见的示例为拦截对象,并在对象的某方法执行前和执行后分别记录日志。

而最常用的拦截方式是使用动态代理类,用其封装一个日志拦截器,当方法被执行时进行日志记录。

日志拦截器类

C#使用Emit构造拦截器动态代理类
 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 }
C#使用Emit构造拦截器动态代理类

被拦截对象类

假设我们有一个Command类,包含一个方法Execute用于执行一些工作。

C#使用Emit构造拦截器动态代理类
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 }
C#使用Emit构造拦截器动态代理类

我们需要在Execute方法执行前和执行后分别记录日志。

动态代理类

C#使用Emit构造拦截器动态代理类
  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 }
C#使用Emit构造拦截器动态代理类

使用动态代理类

C#使用Emit构造拦截器动态代理类
 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 }
C#使用Emit构造拦截器动态代理类

运行结果

C#使用Emit构造拦截器动态代理类

完整代码

下载完整代码