C#のAOPライブラリ(PostSharp)
AOP(Aspect Oriented Programming)?
ログ出力や例外処理など、メソッド全体に共有な処理を重複定義せず一か所に定義したいことがある。
このような共有処理を側面(Aspect)として定義した後、メソッドに適用する手法。
もし、全部それぞれにコピペなどで定義しまった場合。
その部分に修正が入った場合、全てのメソッドを直さないといけなくなり、保守性が低下する。
さらに、本来クラスにさせたい処理でないため、コードの見通しや可読性が低下する。
PostSharp
C#では有名なAOPライブラリ。本来は有料だが、Expressエディションを使えば適用数が制限されるが、無料で実行できる。
(プロジェクト単位:最大10個、ソリューション:最大50個)
なお、最上位版のPostSharp Ultimateを購入すると大体10万円かかるらしい。
PostSharp – the #1 pattern-aware extension to C# and VB
どんなとき使うの?
ドキュメントに記載されている、よくある使用法。
使い方
サンプルクラス
全体処理は以下のようにする。
using System; namespace PostSharpSample { class Program { static void Main(string[] args) { Console.WriteLine("Start Main"); new Sample().Execute(); Console.WriteLine("End Main"); Console.Read(); } } }
今回は以下のSampleクラスのメソッドに対して、メソッド開始・終了にログを出力できるようにする。
using System; namespace PostSharpSample { class Sample { public void Execute() { Console.WriteLine("Hello {0}!", GetVal()); } public string GetVal() { return "PostSharp"; } } }
上記プログラムは、以下の出力になる。
Start Main Hello PostSharp! End Main
AOP属性定義
今回は、メソッド起動時および終了時に起動ログを標準出力する属性を定義する。
using PostSharp.Aspects; using PostSharp.Serialization; using System; namespace PostSharpSample { [PSerializable] class LoggerAttribute : OnMethodBoundaryAspect { public override void OnEntry(MethodExecutionArgs args) { Console.WriteLine("OnEntry {0}", args.Method.Name); } public override void OnExit(MethodExecutionArgs args) { Console.WriteLine("OnExit {0}", args.Method.Name); } } }
適用メソッドに属性付加
using System; namespace PostSharpSample { class Sample { [Logger] public void Execute() { Console.WriteLine("Hello {0}!", GetVal()); } [Logger] public string GetVal() { return "PostSharp"; } } }
すると、AOP適用したプログラムは以下の出力になる。
Start Main OnEntry Execute OnEntry GetVal OnExit GetVal Hello PostSharp! OnExit Execute End Main
参考までに
ドキュメントはこちら。PostSharp Documentation
今回の属性適用によって、ビルド時に適用されたメソッドは以下のようになるらしい。
参照:OnMethodBoundaryAspect Class
int MyMethod(object arg0, int arg1) { OnEntry(); try { // Original method body. OnSuccess(); return returnValue; } catch ( Exception e ) { OnException(); } finally { OnExit(); } }
コンソールアプリ内のSampleクラスをILSpyを用いて逆コンパイルすると以下のようになっていた。
using PostSharp.Aspects; using PostSharp.ImplementationDetails_f584e409; using System; namespace PostSharpSample { internal class Sample { public void Execute() { MethodExecutionArgs methodExecutionArgs = new MethodExecutionArgs(null, null); <>z__a_1.a2.OnEntry(methodExecutionArgs); try { Console.WriteLine("Hello {0}!", this.GetVal()); } catch (Exception exception) { methodExecutionArgs.Exception = exception; throw; } finally { <>z__a_1.a2.OnExit(methodExecutionArgs); } } public string GetVal() { MethodExecutionArgs methodExecutionArgs = new MethodExecutionArgs(null, null); <>z__a_1.a3.OnEntry(methodExecutionArgs); string result; try { string text = "PostSharp"; result = text; } catch (Exception exception) { methodExecutionArgs.Exception = exception; throw; } finally { <>z__a_1.a3.OnExit(methodExecutionArgs); } return result; } } }
この他にも、無料のAOPツールにFodyってのがあるらしいが、上手く動かなかったため後日再チャレンジ予定。