AOP 面向切面程式設計

yueguang發表於2022-01-18

C# AOP 面向切面程式設計之 呼叫攔截

有時候我們需要在程式碼中對方法呼叫進行攔截,並修改引數和返回值,這種操作叫做AOP(面向切面程式設計)
不過需要注意的是,AOP的效率很慢,在需要高效率場合慎用.
以下是C#的AOP方法:

首先建立一個控制檯工程

寫一個calc類,裡面有add個方法:

一會將攔截這個方法,對出參,入參進行檢查.

public class Calc 
{
    public int add(int a, int b)
    {
        return a + b;
    }
}


class Program
{
    static void Main(string[] args)
    {
        Console.Title = "";
        Console.WriteLine(new Calc().add(1, 0));
        Console.WriteLine(new Calc().add(2, 3));
        Console.WriteLine(new Calc().add(1, 1));
        Console.ReadKey(true);
    }
}

執行效果:

非常普通

接著新增一個檔案


裡面的程式碼:
首先寫一個訊息接收器類,用來處理攔截到的呼叫:

/// <summary>
/// AOP方法處理類,實現了IMessageSink介面
/// </summary>
public sealed class MyAopHandler : IMessageSink
{
    /// <summary>
    /// 下一個接收器
    /// </summary>
    public IMessageSink NextSink { get; private set; }
    public MyAopHandler(IMessageSink nextSink)
    {
        this.NextSink = nextSink;
    }

    /// <summary>
    /// 同步處理方法
    /// </summary>
    /// <param name="msg"></param>
    /// <returns></returns>
    public IMessage SyncProcessMessage(IMessage msg)
    {
        //方法呼叫訊息介面
        var call = msg as IMethodCallMessage;

        //只攔截指定方法,其它方法原樣釋放
        if (call == null || (Attribute.GetCustomAttribute(call.MethodBase, typeof(AOPMethodAttribute))) == null || call.MethodName != "add") return NextSink.SyncProcessMessage(msg);

        //判斷第2個引數,如果是0,則強行返回100,不呼叫方法了
        if (((int)call.InArgs[1]) == 0) return new ReturnMessage(100, call.Args, call.ArgCount, call.LogicalCallContext, call);

        //判斷第2個引數,如果是1,則引數強行改為50(失敗了)
        //if (((int)call.InArgs[1]) == 1) call = new MyCall(call, call.Args[0], 50);//方法1  失敗了
        //if (((int)call.InArgs[1]) == 1) call.MethodBase.Invoke(GetUnwrappedServer(), new object[] { call.Args[0], 50 });//方法2 (無法湊夠引數)

        var retMsg = NextSink.SyncProcessMessage(call);

        //判斷返回值,如果是5,則強行改為500
        if (((int)(retMsg as IMethodReturnMessage).ReturnValue) == 5) return new ReturnMessage(500, call.Args, call.ArgCount, call.LogicalCallContext, call);

        return retMsg;
    }

    /// <summary>
    /// 非同步處理方法(暫不處理)
    /// </summary>
    /// <param name="msg"></param>
    /// <param name="replySink"></param>
    /// <returns></returns>
    public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) => null;
}

然後宣告兩個特性,用來指明我們要攔截的Methot,以及它所在的Class:

/// <summary>
/// 貼在方法上的標籤
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class AOPMethodAttribute : Attribute { }

/// <summary>
/// 貼在類上的標籤
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class AOPAttribute : ContextAttribute, IContributeObjectSink
{
    public AOPAttribute() : base("AOP") { }

    /// <summary>
    /// 實現訊息接收器介面
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="next"></param>
    /// <returns></returns>
    public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next) => new MyAopHandler(next);
}

回到Calc類,給類和Methot加上特性標籤:

[AOP]
public class Calc : ContextBoundObject
{
    [AOPMethod]
    public int add(int a, int b)
    {
        return a + b;
    }
}

執行,效果如下:


可以看到返回值已經被攔截修改處理過了

試一下繼承:

[AOP]
public class Calc : ContextBoundObject
{
    [AOPMethod]
    public virtual int add(int a, int b)
    {
        return a + b;
    }
}

public class Calc2 : Calc
{
    public override int add(int a, int b)
    {
        return a + b;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.Title = "";
        Console.WriteLine(new Calc2().add(1, 0));
        Console.WriteLine(new Calc2().add(2, 3));
        Console.WriteLine(new Calc2().add(1, 1));
        Console.ReadKey(true);
    }
}

執行效果:

至此AOP的介紹結束,不過有一點很遺憾,無法修改引數,找了一下午資料無結果,如果誰知道怎麼操作能否回覆告知一下?

原創文章,轉載請註明來源. http://www.cnblogs.com/DragonStart/
 
 

相關文章