c#用特性实现aop_利用C#实现AOP常见的几种方法详解

本文介绍了C#中实现面向切面编程(AOP)的几种常见方法,包括静态织入、动态代理(如Castle.DynamicProxy)以及模拟MVC过滤器的AOP效果。通过这些方法,可以在不修改原有代码的情况下,添加日志记录、错误处理等横切关注点。文章还详细解释了每种方法的工作原理和代码示例。
摘要由CSDN通过智能技术生成

前言

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的中统一处理业务逻辑的一种技术,比较常见的场景是:日志记录,错误捕获、性能监控等

AOP的本质是通过代理对象来间接执行真实对象,在代理类中往往会添加装饰一些额外的业务代码,比如如下代码:

class RealA

{

public virtual string Pro { get; set; }

public virtual void ShowHello(string name)

{

Console.WriteLine($"Hello!{name},Welcome!");

}

}

//调用:

var a = new RealA();

a.Pro = "测试";

a.ShowHello("梦在旅途");

这段代码很简单,只是NEW一个对象,然后设置属性及调用方法,但如果我想在设置属性前后及调用方法前后或报错都能收集日志信息,该如何做呢?可能大家会想到,在设置属性及调用方法前后都加上记录日志的代码不就可以了,虽然这样是可以,但如果很多地方都要用到这个类的时候,那重复的代码是否太多了一些吧,所以我们应该使用代理模式或装饰模式,将原有的真实类RealA委托给代理类ProxyRealA来执行,代理类中在设置属性及调用方法时,再添加记录日志的代码就可以了,这样可以保证代码的干净整洁,也便于代码的后期维护。(注意,在C#中若需被子类重写,父类必需是虚方法或虚属性virtual)

如下代码:

class ProxyRealA : RealA

{

public override string Pro

{

get

{

return base.Pro;

}

set

{

ShowLog("设置Pro属性前日志信息");

base.Pro = value;

ShowLog($"设置Pro属性后日志信息:{value}");

}

}

public override void ShowHello(string name)

{

try

{

ShowLog("ShowHello执行前日志信息");

base.ShowHello(name);

ShowLog("ShowHello执行后日志信息");

}

catch(Exception ex)

{

ShowLog($"ShowHello执行出错日志信息:{ex.Message}");

}

}

private void ShowLog(string log)

{

Console.WriteLine($"{DateTime.Now.ToString()}-{log}");

}

}

//调用:

var aa = new ProxyRealA();

aa.Pro = "测试2";

aa.ShowHello("zuowenjun.cn");

这段代码同样很简单,就是ProxyRealA继承自RealA类,即可看成是ProxyRealA代理RealA,由ProxyRealA提供各种属性及方法调用。这样在ProxyRealA类内部属性及方法执行前后都有统一记录日志的代码,不论在哪里用这个RealA类,都可以直接用ProxyRealA类代替,因为里氏替换原则,父类可以被子类替换,而且后续若想更改日志记录代码方式,只需要在ProxyRealA中更改就行了,这样所有用到的ProxyRealA类的日志都会改变,是不是很爽。

上述执行结果如下图示:

以上通过定义代理类的方式能够实现在方法中统一进行各种执行点的拦截代码逻辑处理,拦截点(或者称为:横切面,切面点)一般主要为:执行前,执行后,发生错误,虽然解决了之前直接调用真实类RealA时,需要重复增加各种逻辑代码的问题,但随之而来的新问题又来了,那就是当一个系统中的类非常多的时候,如果我们针对每个类都定义一个代理类,那么系统的类的个数会成倍增加,而且不同的代理类中可能某些拦截业务逻辑代码都是相同的,这种情况同样是不能允许的,那有没有什么好的办法呢?答案是肯定的,以下是我结合网上资源及个人总结的如下几种常见的实现AOP的方式,各位可以参考学习。

第一种:静态织入,即:在编译时,就将各种涉及AOP拦截的代码注入到符合一定规则的类中,编译后的代码与我们直接在RealA调用属性或方法前后增加代码是相同的,只是这个工作交由编译器来完成。

PostSharp:PostSharp的Aspect是使用Attribute实现的,我们只需事先通过继承自OnMethodBoundaryAspect,然后重写几个常见的方法即可,如:OnEntry,OnExit等,最后只需要在需要进行AOP拦截的属性或方法上加上AOP拦截特性类即可。由于PostSharp是静态织入的,所以相比其它的通过反射或EMIT反射来说效率是最高的,但PostSharp是收费版本的,而且网上的教程比较多,我就不在此重复说明了。

第二种:EMIT反射,即:通过Emit反射动态生成代理类,如下Castle.DynamicProxy的AOP实现方式,代码也还是比较简单的,效率相对第一种要慢一点,但对于普通的反射来说又高一些,代码实现如下:

using Castle.Core.Interceptor;

using Castle.DynamicProxy;

using NLog;

using NLog.Config;

using NLog.Win32.Targets;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace ConsoleApp

{

class Program

{

static void Main(string[] args)

{

ProxyGenerator generator = new ProxyGenerator();

var test = generator.CreateClassProxy(new TestInterceptor());

Console.WriteLine($"GetResult:{test.GetResult(Console.ReadLine())}");

test.GetResult2("test");

Console.ReadKey();

}

}

public class TestInterceptor : StandardInterceptor

{

private static NLog.Logger logger;

protected override void PreProceed(IInvocation invocation)

{

Console.WriteLine(invocation

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值