【代码设计】C# 实现 AOP 面向切面编程

    简单记录一下对AOP的认识,正文为3个部分

    一、AOP由来

    二、用DispatchProxy动态代理实现AOP

    三、通过特性标记,处理多种不同执行前、执行后的逻辑编排

 

一、AOP 由来

    IUserHelper userHelper = new CommonUserHelper();
// commonUser.Create中存在 方法执行前、方法执行后的业务逻辑 userHelper.Create("test0401_A"); public interface IUserHelper { void Create(string name); } public class CommonUserHelper : IUserHelper { private void before() { Console.WriteLine("CommonUser before"); } private void after() { Console.WriteLine("CommonUser after"); } public void Create(string name) { before(); Console.WriteLine($" Common User : {name} Created !"); after(); } }

CommonUserHelper 实现 IUserHelper 接口,假设希望在 Create方法执行前/后写入日志,那就存在这4种业务逻辑:

  ① 执行前写入日志,执行 Create

  ② 执行前写入日志,执行 Create,执行后写入日志

  ③ 执行 Create,执行后写入日志

  ④ 执行 Create

  单一个写日志的需求,就能有4种实现方式,极端情况下,是可以实现 4次 Create 方法;

  如果再加一个数据验证、IP验证、权限验证、异常处理、加入缓存..,那么实现的排列组合方式就更多了,

  无穷尽地加实现、替换类,这显然不是我们希望的。

AOP,Aspect Oriented Programing,是一种编程思维,是对这种缺陷的补充。

 

二、DispatchProxy (动态代理)实现AOP

using System.Reflection;
namespace Cjm.AOP
{
    public class TransformProxy
    {
        public static T GetDynamicProxy<T>(T instance)  
        {
            // DispatchProxy 是system.Reflection封装的类
            // 用以创建实现接口T的代理类CustomProxy的实例
            dynamic obj = DispatchProxy.Create<T, CustomProxy<T>>();
            obj.Instance = instance;
            return (T)obj;
        }
    }

    // DispatchProxy 是抽象类,
    // 实现该类的实例,实例方法执行是会跳转到 Invoke 方法中,
    // 以此达到不破坏实际执行的具体逻辑,而又可以在另外的地方实现执行前、执行后
    public class CustomProxy<T> : DispatchProxy
    {
        public T Instance { get; set; }
        protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
        {
            BeforeProcess();
            var relt = targetMethod?.Invoke(Instance, args);
            AfterProcess();
            return relt;
        }

        private void BeforeProcess()
        {
            Console.WriteLine($"This is BegoreProcess.");
        }

        private void AfterProcess()
        {
            Console.WriteLine($"This is AfterProcess.");
        }
    }
}

    // Main
    IUserHelper userHelper3 = new CommonUserHelper();
    userHelper3 = TransformProxy.GetDynamicProxy(userHelper3);
    userHelper3.Create("test0401_B");

 

三、通过标记特性,处理多种不同的执行前/执行后方法

  此处借用Castle.Core的封装(可通过Nuget管理下载),

  通过实现 StandardInterceptor以重写 执行前/执行后 逻辑的封装方式,

  我们可以更加聚焦在如何处理多种 执行前/执行后 逻辑的编排上。

using Castle.DynamicProxy;
{
    ProxyGenerator proxy = new ProxyGenerator();
    CustomInterceptor customInterceptor = new CustomInterceptor();
    IUserHelper commonUserHelper = new CommonUserHelper();
    var userHelperProxy = proxy.CreateInterfaceProxyWithTarget<IUserHelper>(commonUserHelper, customInterceptor);
    userHelperProxy.Create("TEST0401_C");
}    
    public class CustomInterceptor : StandardInterceptor
    {
        protected override void PreProceed(IInvocation invocation)
        {
            var method = invocation.Method;
            //if (method.IsDefined(typeof(LogBeforeAttribute), true))
            //{
            //    Console.WriteLine("LOG : CustomInterceptor.PreProceed");
            //}

            Action<IInvocation> action = (invocation) => base.PreProceed(invocation);
            // 获取该方法的所有继承BaseAOPAttribute的特性
            var attrs = method.GetCustomAttributes<BaseAOPAttribute>(true);
// 对于 attrs 的排列顺序,可以在特性的实现中增加 int order 属性,在标记特性时写入排序编号
foreach(var attr in attrs) { // 这里是俄罗斯套娃 // 相当于 attr3.AOPAction(invocation, attr2.AOPAction(invocation, attr1.AOPAction(invocation, base.PreProceed(invocation)))) action = attr.AOPAction(invocation, action); } action.Invoke(invocation); } protected override void PerformProceed(IInvocation invocation) { Console.WriteLine("CustomInterceptor.PerformProceed"); base.PerformProceed(invocation); } protected override void PostProceed(IInvocation invocation) { var method = invocation.Method; if (method.IsDefined(typeof(LogAfterAttribute), true)) { Console.WriteLine("LOG : CustomInterceptor.PostProceed"); } base.PreProceed(invocation); } }
    public class LogBeforeAttribute : Attribute {}

    public class LogAfterAttribute : Attribute {}

    public class CheckIPAttribute : BaseAOPAttribute
    {
        public override Action<IInvocation> AOPAction(IInvocation invocation, Action<IInvocation> action)
        {
            return (invocation) => {
                Console.WriteLine("CheckIP ..");
                action.Invoke(invocation); 
}; } }
public abstract class BaseAOPAttribute : Attribute { public abstract Action<IInvocation> AOPAction(IInvocation invocation, Action<IInvocation> action); }

  通过给方法标记特性的方式,达到切面编程的目的(不影响原有实现,而增加实现执行前/执行后的逻辑)

    public interface IUserHelper
    {
        [LogBefore]
        [LogAfter]
        [CheckIP]
        void Create(string name);

        void CreateNoAttri();
    }

 

============================================================

具体的AOP实现上需要考虑的问题多如牛毛,此处仅做简单的思路介绍。

以上主要参考自 B站 朝夕教育 2022 .Net Core AOP实现。

 

原文链接:https://www.cnblogs.com/carmen-019/p/17280096.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:【代码设计】C# 实现 AOP 面向切面编程 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • .NET Core 2.0迁移小技巧之web.config 配置文件示例详解

    首先,我们需要了解什么是“.NET Core”和“web.config”配置文件。”.NET Core” 是一个跨平台的,开源的框架,它使用了不同的部署配置来提高性能。而“web.config”文件是.NET框架中的配置文件,它用于配置ASP.NET应用程序的各个方面,包括Web服务器设置、应用程序设置等。接下来我们会详细讲解如何迁移“.NET Core 2…

    C# 2023年6月3日
    00
  • 基于C#生成随机数示例

    生成随机数是很常见的一种需求,无论是在游戏、金融还是科学领域,都需要使用到随机数。C#作为一门强大的编程语言,自然也提供了非常方便的方法来生成随机数。 下面是生成随机数的完整攻略。 步骤一 – 声明随机数生成器 首先,我们需要创建 Random 类型的对象,来帮助我们生成随机数。 在 C# 中,我们可以使用以下代码生成随机数生成器: Random rando…

    C# 2023年6月1日
    00
  • C# 遍历文件夹子目录下所有图片及遍历文件夹下的文件

    C# 中遍历文件夹和子目录很常见,本文就详细讲解如何使用 C# 遍历文件夹中的文件以及子目录中的文件,同时只选择图片文件。 遍历文件夹中的所有图片文件 方法一:使用 Directory.GetFiles Directory.GetFiles() 方法返回指定路径下的所有文件,可以通过 fileName.Contains(“.jpg”) 和 fileName.…

    C# 2023年6月1日
    00
  • C#使用标签软件Bartender打印标签模板

    下面是C#使用标签软件Bartender打印标签模板的完整攻略: 1. 引入Bartender SDK 首先需要在C#工程中引入Bartender SDK。在 Visual Studio 中,打开项目 Solution Explorer,右键点击引用目录,选择添加引用,找到刚刚安装的 Bartender SDK 程序文件夹下的 “Interop.Seagul…

    C# 2023年6月7日
    00
  • C# 中将数值型数据转换为字节数组的方法

    将数值型数据转换成字节数组在 C# 中是一项常见的任务。处理二进制数据通常需要将二进制数据以原始字节数组的形式进行处理。这篇攻略将提供如何在 C# 中将数值型数据转换为字节数组的方法。 方法一:BitConverter.GetBytes 其中一个将数值型数据转换成字节数组的方法是通过使用 BitConverter 类。该方法可以将数值类型转换成一个字节数组,…

    C# 2023年6月7日
    00
  • VS2008中使用JavaScript调用WebServices

    VS2008中使用JavaScript调用WebServices的完整攻略 在VS2008中,我们可以使用JavaScript调用WebServices。本文将提供详细的“VS2008中使用JavaScript调用WebServices”的完整攻略,包括如何创建WebServices、如何使用JavaScript调用WebServices以及两个示例。 创建…

    C# 2023年5月15日
    00
  • C#设计模式之行为型模式详解

    C#设计模式之行为型模式详解 什么是行为型模式 行为型模式是面向对象设计中的一类设计模式,主要关注对象之间的交互和通信,以及对象的职责分配。它们描述了对象的行为,而不是它们的标识或状态。这些模式涉及到算法与对象间职责的分配,其中包括职责链、命令、解释器、迭代器、中介者、回调、观察者、状态、策略和模板方法等实现方法。 为什么需要行为型模式 在开发软件的过程中,…

    C# 2023年5月31日
    00
  • 如何运行编译.NetCore的源码?

    作为.net的开发人员,为了能更好的code,我们要知其然并知其所以然,了解.netcore的源码是我们的基本素养✊ 源码地址 .NET Platform (github.com) 这个是.net在github上开源的源码地址aspnetcore 这个是.netcore的源码地址runtime 这个是运行时的源码地址,有些.netcore源码会依赖此运行时(…

    C# 2023年4月24日
    00
合作推广
合作推广
分享本页
返回顶部