在 C# 上使用 AOP
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();
執行後一個會列出函式名稱,另一個則是印出錯誤訊息。
參考資料