【代码设计】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日

相关文章

  • Unity实现绕任意轴任意角度旋转向量

    首先,在Unity中实现绕任意轴旋转向量需要使用Quaternion类,其提供了一些方法可以实现旋转。具体步骤如下: 步骤一:创建旋转Quaternion 使用Quaternion类的静态方法Quaternion.AngleAxis可以创建一个旋转的Quaternion,其方法的两个参数分别为旋转角度和旋转轴。 float angle = 30.0f; Ve…

    C# 2023年6月3日
    00
  • C#实现简单飞行棋小游戏

    首先来讲一下“C#实现简单飞行棋小游戏”的完整攻略。 简介 飞行棋,是一种以飞行为主题的棋类游戏,是一种常见的亲子游戏。游戏规则简单、易于上手,非常适合大众化的群体。 游戏规则 游戏地图共有 100 个格子,分别标记着不同的内容,如酒驾、炸弹、地雷、停机坪、幸福、喜事等等。同时,每个玩家有 4 个棋子,起点和终点不同,各自从起点进入,经过终点,返回起点,先完…

    C# 2023年6月7日
    00
  • C#使用二维数组模拟斗地主

    C#使用二维数组模拟斗地主攻略 什么是二维数组 二维数组是数组的一种,与一维数组不同的是,它包含两个维度(行和列)。在编程中,可以使用二维数组来表示多个变量,比如一个矩阵。二维数组的定义方式如下: int[,] arr = new int[3,4]; 表示定义了一个由 3 行 4 列的整型数组。 斗地主游戏规则 斗地主是一种比较流行的扑克牌游戏。游戏有三个玩…

    C# 2023年6月7日
    00
  • C#编程实现动态改变配置文件信息的方法

    C#编程实现动态改变配置文件信息的方法 在C#应用程序中,我们经常使用配置文件来存储一些重要的数据或者一些配置信息。但是,有时候我们需要动态地修改配置文件的信息,例如在程序运行时读取当前登录用户的信息并保存到配置文件中。本文将详细讲解如何在C#应用程序中动态地修改配置文件信息。 步骤一:引入命名空间 在程序中使用XmlDocument类和XmlTextWri…

    C# 2023年6月1日
    00
  • C#实现定时关机小应用

    针对” C#实现定时关机小应用”,我们可以使用System.Diagnostics 命名空间中的Process类来实现。 首先,我们需要一个定时器来控制时间: using System.Windows.Forms; using System.Diagnostics; namespace ShutdownApp { public partial class M…

    C# 2023年6月1日
    00
  • c# 可疑文件扫描代码(找到木马)(简)

    下面我将详细讲解“c# 可疑文件扫描代码(找到木马)(简)”的完整攻略。 准备工作 在开始进行代码的编写之前,我们需要先准备好以下工具和环境: C# 开发环境:如 Visual Studio; 病毒库:可以通过 GitHub 等平台下载; 测试病毒程序:用于模拟实际的病毒程序。 实现过程 我们将使用 C# 语言来编写一个简单的可疑文件扫描工具,具体实现过程如…

    C# 2023年6月1日
    00
  • 老生常谈.NET中的 COM 组件

    COM(Component Object Model) 是一种基于二进制的软件组件技术,它可用于跨语言和跨机器边界提供组件交互,是一种早期的应用程序组件化技术。在 .NET 开发中,我们可以使用 COM 组件来实现和调用外部非 .NET 的框架或组件。 COM 组件简介 COM 组件是一种通过二进制接口进行交互的组件,其二进制接口包括方法、属性、事件等。CO…

    C# 2023年6月3日
    00
  • C#异常处理详解

    下面我将详细讲解“C#异常处理详解”的完整攻略。 什么是异常? 异常(Exception)是指在程序执行过程中出现的错误状况,如内存不足、数组越界、数学计算异常等。这些错误状况可能会导致程序异常终止,而异常处理就是解决这些问题的方法。 异常处理的方法 在C#中,我们可以使用try-catch语句来处理异常。try块中放置我们要执行的代码,如果在执行过程中出现…

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