欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C# 使用Emit实现动态AOP框架 (一)

程序员文章站 2022-06-28 20:13:15
最近需要有一个完全自主的基于C#语言的Aop框架,查了一下资料实现方式主要分为:静态织入和动态代理,静态织入以Postshop为代表,而动态代理又分为: 1、普通反射 2、Emit反射 3、微软提供的.Net Remoting和RealProxy (微软官方例子https://msdn.micros ......

        最近需要有一个完全自主的基于c#语言的aop框架,查了一下资料实现方式主要分为:静态织入和动态代理,静态织入以postshop为代表,而动态代理又分为:

1、普通反射

2、emit反射

3、微软提供的.net remoting和realproxy

(微软官方例子)

      总体来说静态织入速度最快,普通反射最慢,而.net remoting和realprox实现起来又相对较复杂。而emit速度居中,同时其一次生成后,将结果序列化,速度也并不慢,同时和原有类并没有紧密耦合,通过外部配置文件可以方便的控制要进行代理的类型、方法和属性,其缺点是被代理的方法、属性必须为virtual类型。

一、被代理类和代理类

       被代理类,是我们正常使用的类,里边是原有的业务逻辑,只要在被代理方法上申明上相应的切面特性就行了,使用起来比较简单;如下

 1   public class aoptest
 2   {
 3         
 4         public aoptest()
 5         {
 6             name = "小明"; age = 10;
 7         }
 8 
 9         public aoptest(string name, int age)
10         {
11             name = name; age = age;
12         }
13 
14         [log]
15         public virtual string name { get; set; }
16 
17         [log]
18         public virtual int age { get; set; }
19 
20         [log]
21         public virtual int nyearlater(int a)
22         {
23             int larter = age + a;
24 
25             return larter;
26         }
27     }

 

     代理类是aop框架自动生成的类,使用反编译工具我们可以看到,它比被代理类多了切面上下文声明(aspectcontent)和相应的切面特性对象声明,在被代理类的方法执行前后,相应切面特性调用onentry、onexit执行相关操作,如日志、参数验证、权限验证等等aop功能,其中aspectcontext是onentry、onexit调用参数,如下:

public class aoptest_proxy : aoptest
{
    public override string name
    {
        get
        {
            object[] args = new object[0];
            aspectcontext aspectcontext = new aspectcontext(this, "get_name", args);
            logattribute logattribute = new logattribute();
            logattribute.onentry(aspectcontext);
            string name = base.name;
            aspectcontext.result = name;
            logattribute.onexit(aspectcontext);
            return name;
        }
        set
        {
            aspectcontext context = new aspectcontext(this, "set_name", new object[]
            {
                value
            });
            logattribute logattribute = new logattribute();
            logattribute.onentry(context);
            base.name = value;
            logattribute.onexit(context);
        }
    }

    public override int age
    {
        get
        {
            object[] args = new object[0];
            aspectcontext aspectcontext = new aspectcontext(this, "get_age", args);
            logattribute logattribute = new logattribute();
            logattribute.onentry(aspectcontext);
            int age = base.age;
            aspectcontext.result = age;
            logattribute.onexit(aspectcontext);
            return age;
        }
        set
        {
            aspectcontext context = new aspectcontext(this, "set_age", new object[]
            {
                value
            });
            logattribute logattribute = new logattribute();
            logattribute.onentry(context);
            base.age = value;
            logattribute.onexit(context);
        }
    }

    public aoptest_proxy(string name, int age) : base(name, age)
    {
    }

    public override int nyearlater(int num)
    {
        aspectcontext aspectcontext = new aspectcontext(this, "nyearlater", new object[]
        {
            num
        });
        logattribute logattribute = new logattribute();
        logattribute.onentry(aspectcontext);
        int num2 = base.nyearlater(num);
        aspectcontext.result = num2;
        logattribute.onexit(aspectcontext);
        return num2;
    }
}

 

二、测试方法

public static void test()
{
  try
  {
     aoptest withpara = dynamicproxy.create<aoptest>("lxl", 10); ;
 
     withpara.nyearlater(10);
     console.writeline("done...");
   }
   catch (exception ex)
   {
      console.writeline(ex.message);
   }
 }

 

测试方法中:aoptest withpara = dynamicproxy.create<aoptest>("lxl", 10); ,生成一个代理对象,其他就正常使用就可以了。

调用测试方法执行结果如下:

log onentry:set_name(lxl)
log onexit: set_name

log onentry:set_age(10)
log onexit: set_age

log onentry:nyearlater(10)
log onentry:get_age()
log onexit: get_age result: 10

log onexit: nyearlater result: 20

done...

 

   通过结果可以看到 属性name、age的set方法,nyearlater方法,以及age属性的get方法都实现了日志记录。下边将分几篇来详细介绍dynamicproxy类的实现。欢迎大家多多指正、交流。