序言

本篇文章介绍基于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”。代码如下:

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World");
}
}

  

AOP需求:需要在打印前和打印后输出当前时间。

-添加Mono.Cecil.dll引用并添加以下代码

using Mono.Cecil;
using Mono.Cecil.Cil;

  

-定位方法(建议用Linq)

        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

var worker = method.Body.GetILProcessor();//Get IL

-AOP Front

                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

                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) }))));

  

-保存修改

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[];//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 - ]; //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[];//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 - ]; //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

-结果

                 

Mono.Cecil 初探(一):实现AOP的更多相关文章

  1. C# Asp.net中的AOP框架 Microsoft.CCI, Mono.Cecil, Typemock Open-AOP API, PostSharp -摘自网络 (可以利用反射 Attribute 进行面向切面编程 可以用在记录整个方法的Log方面)

    Both Microsoft.CCI and Mono.Cecil are low-level, and don't validate produced assemblies. It takes lo ...

  2. 基于Mono.Cecil的静态注入

    Aop注入有2种方式:动态注入和静态注入,其中动态注入有很多实现了 动态注入有几种方式: 利用Remoting的ContextBoundObject或MarshalByRefObject. 动态代理( ...

  3. 编译时MSIL注入--实践Mono Cecil(1)

    原文:编译时MSIL注入--实践Mono Cecil(1) 紧接上两篇浅谈.NET编译时注入(C#-->IL)和浅谈VS编译自定义编译任务—MSBuild Task(csproject),在第一 ...

  4. 运用Mono.Cecil 反射读取.NET程序集元数据

    CLR自带的反射机智和API可以很轻松的读取.NET程序集信息,但是不能对程序集进行修改.CLR提供的是只读的API,但是开源项目Mono.Cecil不仅仅可以读取.NET程序集的元数据,还可以进行修 ...

  5. 使用Mono Cecil 动态获取运行时数据 (Atribute形式 进行注入 用于写Log) [此文报考 xxx is declared in another module and needs to be imported的解决方法]-摘自网络

    目录 一:普通写法 二:注入定义 三:Weave函数 四:参数构造 五:业务编写 六:注入调用 7.  怎么调用别的程序集的方法示例 8. [is declared in another module ...

  6. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

  7. 教你怎么用Mono Cecil - 动态注入 (注意代码的注释)

    原文 教你怎么用Mono Cecil - 动态注入 (注意代码的注释) 使用 Mono Cecil 进行反编译:using Mono.Cecil; using Mono.Cecil.Cil; //.. ...

  8. 使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试

    Unity3D 引擎在  UnityEngine 名字空间下,提供了  Profiler 类(Unity 5.6 开始似乎改变了这个名字空间),用于辅助对项目性能进行测试.以 Android 平台为例 ...

  9. 巧用Mono.Cecil反射加载类型和方法信息

    最近在做服务的细粒度治理,统一管理所有服务的方法.参数.返回值信息.方便后续的各个模块之间的对接和协作. 目前系统中所有的服务,管理到接口契约粒度,即服务接口声明和服务接口实现.要做服务的细粒度治理: ...

随机推荐

  1. 安卓调用百度地图api 错误 mcode参数不存在

    自己的手机app里用到了百度地图sdk,希望根据手机获得的坐标来逆向到百度地图的坐标. 根据api文档拼写了url,因为是移动端,说是要添加mcode参数,然后我的url看起来如下: http://a ...

  2. 持续集成及部署利器:Go

    Go是一款先进的持续集成和发布管理系统,由ThoughtWorks开发.(不要和Google的编程语言Go混淆了!)其前身为CruiseControl,是ThoughtWorks在做咨询和交付交付项目 ...

  3. 让浏览器不再显示 https 页面中的 http 请求警报

    HTTPS 是 HTTP over Secure Socket Layer,以安全为目标的 HTTP 通道,所以在 HTTPS 承载的页面上不允许出现 http 请求,一旦出现就是提示或报错: Mix ...

  4. 分辨率、DPI、PPI和屏幕尺寸,你都知道是啥么?

    分辨率.DPI.PPI和屏幕尺寸 分辨率 DPI/PPI 坑爹的屏幕尺寸 Reference 手机开发中不免会遇到分辨率.DPI.PPI和屏幕尺寸等术语,那就弄弄清楚这些概念的真正含义. 分辨率 分辨 ...

  5. Eclipse 调试技巧

    条件断点 顾名思义,是指当发生某种情况或者触发某种条件的情况下命中断点.常用的情形就是for循环中某个变量为xx值的时候命中断点类似的. 做法1:在debug视图中,BreakPoint View将所 ...

  6. php后台修改人员表信息

    显示info人员表里所有内容 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "h ...

  7. MongoDB 文档的删除操作

    在db中删除数据是十分危险的事,建议使用logic delete,即在doc中增加一个field:IsDeleted,将其设置为1,表示该doc在逻辑上被删除,这种workaround将delete操 ...

  8. 解密jQuery事件核心 - 自定义设计(三)

    接上文http://www.cnblogs.com/aaronjs/p/3447483.html 本文重点:自定义事件 “通过事件机制,可以将类设计为独立的模块,通过事件对外通信,提高了程序的开发效率 ...

  9. 深入理解DOM事件类型系列第五篇——文本事件

    × 目录 [1]change [2]textInput [3]input[4]propertychange[5]兼容 前面的话 如果DOM结构发生变化,触发的是变动事件:如果文本框中的文本发生变化,触 ...

  10. Mac下有道笔记本问题反馈

    1).Mac笔记上的编辑状态框非常的小.操作起来不是非常的方便.可以把显示稍微放大一些. 2). 新建笔记本的时候,这里用户可能没有注意到这里可以输入,此时这里的高亮的颜色可以适当的修改成别的颜色. ...