Mono.Cecil 初探(一):实现AOP
序言
本篇文章介绍基于Mono.Cecil实现静态AOP的两种方式:无交互AOP和交互式AOP。
概念介绍
Mono.Cecil:一个可加载并浏览现有程序集并进行动态修改并保存的.NET框架。
AOP:面向切面编程。可以简单理解为程序中的每个类的方法均是一块“积木”,采用AOP把新增的“积木随心所欲地嵌入”到各个“积木”上面(前面)或下面(后面)。如下图所示:
动态AOP:在运行时进行AOP。.NET现有.Net Remoting,Unity,Spring.NET,PostSharp,Mr Advice等多种框架可供使用。
静态AOP:相对于动态AOP,静态AOP是指在编译时、运行前就已经进行了AOP。.NET中一般通过修改编译生成的中间语言IL实现,Mono.Cecil就是实现静态AOP一个很好的方式。根据与原有程序交互的情况,本文把静态AOP分为无交互AOP和交互式AOP两种方式。
无交互AOP
与原有程序无任何“交集”,纯粹式的AOP。下面通过两个例子进行说明如何通过Mono.Cecil进行无交互AOP。
同一个方法内AOP
原程序:控制台打印出“Hello World”。代码如下:
1 2 3 4 5 6 7 | class Program { static void Main( string [] args) { Console.WriteLine( "Hello World" ); } } |
AOP需求:需要在打印前和打印后输出当前时间。
-添加Mono.Cecil.dll引用并添加以下代码
1 2 | using Mono.Cecil; using Mono.Cecil.Cil; |
-定位方法(建议用Linq)
1 2 3 4 | AssemblyDefinition assembiy = AssemblyDefinition.ReadAssembly(Path); //Path: dll or exe Path var method = assembiy.MainModule .Types.FirstOrDefault(t => t.Name == "Program" ) .Methods.FirstOrDefault(m => m.Name == "Main" ); |
-获取IL
1 | var worker = method.Body.GetILProcessor(); //Get IL |
-AOP Front
1 2 3 4 5 | string FrontTime = DateTime.Now.ToString(); var ins = method.Body.Instructions[0]; //Get First IL Step worker.InsertBefore(ins, worker.Create(OpCodes.Ldstr, FrontTime)); worker.InsertBefore(ins, worker.Create(OpCodes.Call, assembiy.MainModule.Import( typeof (Console).GetMethod( "WriteLine" , new Type[] { typeof ( string ) })))); |
-AOP Back
1 2 3 4 5 | string BackTime = DateTime.Now.ToString(); ins = method.Body.Instructions[method.Body.Instructions.Count - 1]; //Get Lastest IL Step worker.InsertBefore(ins, worker.Create(OpCodes.Ldstr, BackTime)); worker.InsertBefore(ins, worker.Create(OpCodes.Call, assembiy.MainModule.Import( typeof (Console).GetMethod( "WriteLine" , new Type[] { typeof ( string ) })))); |
-保存修改
1 | assembiy.Write(Path); |
-结果
采用Refactor进行对比得知AOP已成功!
“跨类”AOP
此种方式指的是在方法前后通过指定调用其他类的方法实现AOP,可用于扩展功能,如日志记录,数据库记录等。
相对于同一个方法内的AOP,因为通过方法调用指定类的方法,实现更加灵活,功能扩展更加全面且在开发阶段可进行调试或单元测试,所以此种方式应用层面更为广泛。
下面将从静态和非静态两种方式进行代码实现。
原程序:控制台打印出“Hello World”
class Program { static void Main(string[] args) { Console.WriteLine("Hello World"); } }
AOP需求:需要把打印前的时间和打印后的时间记录到数据库中。
跨静态类/静态方法AOP
为示例简便,LogDT方法表示记录时间到数据库中(为方便显示,采用控制台打印的方式)
public class LogDateTime_Static { public static void LogDT() { Console.WriteLine(DateTime.Now.ToString()); } }
-AOP Front
//AOP_Front var ins = method.Body.Instructions[0];//Get First IL Step worker.InsertBefore(ins, worker.Create(OpCodes.Call, assembiy.MainModule.Import(typeof(LogDateTime).GetMethod("LogDT"))));//Call static Method
-AOP Back
//AOP_Back ins = method.Body.Instructions[method.Body.Instructions.Count - 1]; //Get Lastest IL Step worker.InsertBefore(ins, worker.Create(OpCodes.Call, assembiy.MainModule.Import(typeof(LogDateTime).GetMethod("LogDT"))));//Call static Method
-结果
跨非静态类AOP
非静态类实例代码如下:
public class LogDateTime_NonStatic { public void LogDT() { Console.WriteLine(DateTime.Now.ToString()); } }
-实例化指定类
var Constructor = assembiy.MainModule.Import(typeof(LogDateTime_NonStatic).GetConstructor(new Type[] { }));//Create Instance
-AOP Front
var ins = method.Body.Instructions[0];//Get First IL Step worker.InsertBefore(ins, worker.Create(OpCodes.Newobj, Constructor)); worker.InsertBefore(ins, worker.Create(OpCodes.Call, assembiy.MainModule.Import(typeof(LogDateTime_NonStatic).GetMethod("LogDT"))));////Call Instance Method
-AOP Back
ins = method.Body.Instructions[method.Body.Instructions.Count - 1]; //Get Lastest IL Step worker.InsertBefore(ins, worker.Create(OpCodes.Newobj, Constructor)); worker.InsertBefore(ins, worker.Create(OpCodes.Call, assembiy.MainModule.Import(typeof(LogDateTime_NonStatic).GetMethod("LogDT"))));////Call Instance Method
-结果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统