AOP 的定義這邊就不詳述,簡單地說就是像讓 C# 跟 Python 一樣有 decorator 的功能,像是以下的 Python 程式碼:

def print_func_name(func):
    def warp():
        print("Now use function '{}'".format(func.__name__))
        func()
    return warp


@print_func_name
def dog_bark():
    print("Bark !!!")

這樣在執行 dog_bark 時就會印象函式名稱,而在 C# 並沒有類似的原生功能,早期有 PostSharp,但它的原理是干涉編譯器的行為,而且它是商用。不過好在 .Net Core 之後的版本,有熱心人士做相關套件,像是 isRock.Core.AOP。

在安裝後撰寫下列類別:

public class Logging : PolicyInjectionAttributeBase
{
    public string LoggingFileName { get; set; } = string.Empty;

    public override void AfterInvoke(object sender, PolicyInjectionAttributeEventArgs e)
    {
        //base.AfterInvoke(sender, e);
        //Type senderType = sender.GetType();
        Console.WriteLine($"method: {((MethodInfo)sender).Name}");
    }

    public override void OnException(object sender, PolicyInjectionAttributeEventArgs e)
    {
        Exception ex = e.Exception;
        if (ex != null) 
        {
            Console.WriteLine("Exception Message:");
            Console.WriteLine(ex.StackTrace);
        }
        e.ExceptionHandled = true;
    }
}

基本上就是實作它的 event,上述程式碼有兩個行為,一個是呼叫後印出函式名稱,另一個是做錯誤處理。

而測試用的類別如下:

public interface IService
{
    void Exec();
    void Exec2();
}

public class TestService : IService
{
    public static void TestFunc()
    {
        IService service = PolicyInjection.Create<IService>(new TestService());
        service.Exec2();
        Console.WriteLine("TestFunc called.");
    }

    [Logging(LoggingFileName = "")]
    public void Exec()
    {
        Console.WriteLine("TestService called.");
    }

    [Logging(LoggingFileName = "")]
    public void Exec2()
    {
        throw new NotImplementedException();
    }
}

測試的程式碼如下:

IService service = PolicyInjection.Create<IService>(new TestService());
service.Exec();
service.Exec2();

執行後一個會列出函式名稱,另一個則是印出錯誤訊息。

參考資料