AOP即面向切面编程,核心思想是将与业务逻辑无关的通用功能,比如日志记录、异常捕获、性能监控等,从业务代码中分离出来,通过切面的方式统一织入到目标方法中,避免代码重复,提升项目的可维护性和扩展性。在C#生态中,PostSharp和Castle DynamicProxy是实现AOP的两种常用方案,二者的实现原理和使用场景各有不同。

PostSharp实现AOP的方式
PostSharp是基于编译时织入的AOP框架,它在项目编译阶段就会将切面逻辑直接注入到目标程序集的IL代码中,运行时不需要额外的代理对象,性能开销较小。使用PostSharp需要先通过NuGet安装PostSharp包,然后定义切面特性并应用到目标方法上。
定义日志切面示例
首先创建一个继承自OnMethodBoundaryAspect的切面类,重写方法执行前后的回调方法:
using PostSharp.Aspects;
using System;
[Serializable]
public class LogAspect : OnMethodBoundaryAspect
{
// 方法执行前调用
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine($"方法 {args.Method.Name} 开始执行,参数:{string.Join(",", args.Arguments)}");
}
// 方法执行后调用
public override void OnExit(MethodExecutionArgs args)
{
Console.WriteLine($"方法 {args.Method.Name} 执行结束");
}
// 方法抛出异常时调用
public override void OnException(MethodExecutionArgs args)
{
Console.WriteLine($"方法 {args.Method.Name} 执行异常:{args.Exception.Message}");
args.FlowBehavior = FlowBehavior.Continue; // 标记异常已处理,不向上抛出
}
}
使用切面特性
直接在需要添加日志逻辑的业务方法上标注[LogAspect]特性即可:
public class UserService
{
[LogAspect]
public void AddUser(string userName, int age)
{
Console.WriteLine($"添加用户:{userName},年龄:{age}");
// 模拟业务逻辑
if (age < 0)
{
throw new ArgumentException("年龄不能为负数");
}
}
}
调用AddUser方法时,会自动触发切面的日志输出和异常处理逻辑,不需要在业务代码中手动编写这些通用逻辑。
Castle DynamicProxy实现AOP的方式
Castle DynamicProxy是基于运行时动态代理的AOP框架,它会在运行时动态生成目标对象的代理类,通过拦截代理对象的方法调用来实现切面逻辑,不需要修改编译后的程序集,灵活性更高。使用它需要先通过NuGet安装Castle.Core包。
定义拦截器示例
首先实现IInterceptor接口,编写拦截逻辑:
using Castle.DynamicProxy;
using System;
public class LogInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
// 方法执行前逻辑
Console.WriteLine($"方法 {invocation.Method.Name} 开始执行,参数:{string.Join(",", invocation.Arguments)}");
try
{
// 执行原方法
invocation.Proceed();
// 方法执行后逻辑
Console.WriteLine($"方法 {invocation.Method.Name} 执行结束");
}
catch (Exception ex)
{
// 异常处理逻辑
Console.WriteLine($"方法 {invocation.Method.Name} 执行异常:{ex.Message}");
}
}
}
创建代理对象调用方法
Castle DynamicProxy要求目标类不能是密封类,且被拦截的方法需要是虚方法,或者通过接口的方式实现:
// 定义业务接口
public interface IUserService
{
void AddUser(string userName, int age);
}
// 实现业务接口
public class UserService : IUserService
{
public virtual void AddUser(string userName, int age)
{
Console.WriteLine($"添加用户:{userName},年龄:{age}");
if (age < 0)
{
throw new ArgumentException("年龄不能为负数");
}
}
}
class Program
{
static void Main(string[] args)
{
// 创建代理生成器
ProxyGenerator proxyGenerator = new ProxyGenerator();
// 创建拦截器实例
LogInterceptor interceptor = new LogInterceptor();
// 生成代理对象
IUserService userService = proxyGenerator.CreateInterfaceProxyWithTarget<IUserService>(new UserService(), interceptor);
// 调用代理对象的方法,会自动触发拦截器逻辑
userService.AddUser("张三", 20);
Console.WriteLine("-----");
userService.AddUser("李四", -5);
}
}
两种方案的对比
PostSharp和Castle DynamicProxy各有适用场景,具体差异如下:
| 对比维度 | PostSharp | Castle DynamicProxy |
|---|---|---|
| 织入时机 | 编译时 | 运行时 |
| 性能开销 | 较小,运行时无额外代理创建开销 | 稍大,运行时需要创建代理对象 |
| 使用复杂度 | 简单,直接标注特性即可 | 稍复杂,需要手动创建代理对象 |
| 灵活性 | 较低,切面逻辑编译后固定 | 较高,运行时可动态调整拦截逻辑 |
| 适用场景 | 通用切面逻辑固定、追求性能的项目 | 需要动态控制切面逻辑、灵活性要求高的项目 |
入门选择建议
如果是刚接触C# AOP的新手,且项目中通用逻辑比较固定,优先选择PostSharp,上手更快,配置简单。如果项目需要运行时动态调整切面逻辑,或者不想依赖编译时织入的框架,Castle DynamicProxy是更合适的选择。两种方案都能有效实现AOP的核心目标,开发者可以根据项目实际需求选择使用。
AOPCastle_DynamicProxyPostSharpC#修改时间:2026-07-21 18:33:31