序言

本篇文章介绍基于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. actor concurrency

    The hardware we rely on is changing rapidly as ever-faster chips are replaced by ever-increasing num ...

  2. iOS block种类和切换

    block 分为三种 NSGlobalBlock,NSStackBlock, NSMallocBlock. NSGlobalBlock:类似函数,位于text段: NSStackBlock:位于栈内存 ...

  3. 可扩容分布式session方案

    分布式session有以下几种方案: 1. 基于nfs(net filesystem)的session共享 将共享服务器目录mount各服务器的本地session目录,session读写受共享服务器i ...

  4. zookeeper分布式锁原理

    一.分布式锁介绍分布式锁主要用于在分布式环境中保护跨进程.跨主机.跨网络的共享资源实现互斥访问,以达到保证数据的一致性. 二.架构介绍在介绍使用Zookeeper实现分布式锁之前,首先看当前的系统架构 ...

  5. PHP类的封装和做投票和用进度条显示

    三处理传过来的数据1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:// ...

  6. GitHub的使用记录

    前言: 该贴为笔者在使用GItHub中的一些使用注意,及Git的基本命令,会一直记录笔者在使用GitHub中可能产生的错误及解决方法(会一直更新中),待一些Git初使用者参考,如果有说明不详细或不对的 ...

  7. ASP.NET列表信息以Excel形式导出

    1.从数据查出数据扔进table中: private DataTable getTable() { var dbHelper = applyBLL.CreateDataBase("VISAd ...

  8. WCF 安全性 之 None

    案例下载 http://download.csdn.net/detail/woxpp/4113172 服务端配置代码 <system.serviceModel> <services& ...

  9. Drupal8重命名上传的中文名文件

    完整的模块代码文件在Coding.net上,想直接使用的请前往下载:https://coding.net/u/yamus/p/chinese_rename/git/tree/master 最近吧Dru ...

  10. int and string

    int转string一.#include <sstream> int n = 0; std::stringstream ss; std::string str; ss<<n; ...