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

 

-结果

                         

 

posted @   蜡笔小黄  阅读(8808)  评论(3编辑  收藏  举报
编辑推荐:
· .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 接口并集成到在线客服系统
点击右上角即可分享
微信分享提示