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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

在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

下载完整代码






本文转自匠心十年博客园博客,原文链接:http://www.cnblogs.com/gaochundong/archive/2013/06/01/csharp_emit_create_interceptor_proxy.html,如需转载请自行联系原作者

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2月前
|
开发框架 .NET C#
C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式
【10月更文挑战第9天】在 C#/.NET Core 中,有多种方法可以删除字符串的最后一个字符,包括使用 `Substring` 方法、`Remove` 方法、`ToCharArray` 与 `Array.Copy`、`StringBuilder`、正则表达式、循环遍历字符数组以及使用 LINQ 的 `SkipLast` 方法。
|
3月前
|
存储 C# 索引
C# 一分钟浅谈:数组与集合类的基本操作
【9月更文挑战第1天】本文详细介绍了C#中数组和集合类的基本操作,包括创建、访问、遍历及常见问题的解决方法。数组适用于固定长度的数据存储,而集合类如`List<T>`则提供了动态扩展的能力。文章通过示例代码展示了如何处理索引越界、数组长度不可变及集合容量不足等问题,并提供了解决方案。掌握这些基础知识可使程序更加高效和清晰。
81 2
|
2月前
|
Java 程序员 C#
【类的应用】C#应用之派生类构造方法给基类构造方法传参赋值
【类的应用】C#应用之派生类构造方法给基类构造方法传参赋值
13 0
|
3月前
|
C# 数据安全/隐私保护
C# 一分钟浅谈:类与对象的概念理解
【9月更文挑战第2天】本文从零开始详细介绍了C#中的类与对象概念。类作为一种自定义数据类型,定义了对象的属性和方法;对象则是类的实例,拥有独立的状态。通过具体代码示例,如定义 `Person` 类及其实例化过程,帮助读者更好地理解和应用这两个核心概念。此外,还总结了常见的问题及解决方法,为编写高质量的面向对象程序奠定基础。
27 2
|
4月前
|
C#
C#中的类和继承
C#中的类和继承
44 6
|
4月前
|
Java C# 索引
C# 面向对象编程(一)——类
C# 面向对象编程(一)——类
35 0
|
4月前
|
开发框架 .NET 编译器
C# 中的记录(record)类型和类(class)类型对比总结
C# 中的记录(record)类型和类(class)类型对比总结
|
6月前
|
开发框架 .NET 编译器
程序与技术分享:C#基础知识梳理系列三:C#类成员:常量、字段、属性
程序与技术分享:C#基础知识梳理系列三:C#类成员:常量、字段、属性
37 2
|
6月前
|
C#
C# 版本的 计时器类 精确到微秒 秒后保留一位小数 支持年月日时分秒带单位的输出
这篇2010年的文章是从别处搬运过来的,主要包含一个C#类`TimeCount`,该类有多个方法用于处理时间相关的计算。例如,`GetMaxYearCount`计算以毫秒为单位的最大年数,`GetCurrentTimeByMiliSec`将当前时间转换为毫秒,还有`SecondsToYYMMDDhhmmss`将秒数转换为年月日时分秒的字符串。此外,类中还包括一些辅助方法,如处理小数点后保留一位数字的`RemainOneFigureAfterDot`。
|
6月前
|
存储 安全 C#
C# 类的深入指南
C# 类的深入指南