Status Code 303 - See Other

Java, C, C++, C#, Objective-C, Swift, bash, perl, ruby, PHP, Python, Scala, Groovy, Go, DevOps, Raspberry Pi など。情報の誤りや指摘・意見などは自由にどうぞ。

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

どんなとき使うの?

ドキュメントに記載されている、よくある使用法。

  • プロパティ値が変更されたときの通知
  • UndoとRedo
  • 契約プログラミング(事前条件/事後条件確認)
  • ログ・監視・プロファイリング
  • 例外発生時の制御
  • トランザクション制御
  • マルチスレッド時の挙動制御(バックグランド・フォアグランド制御、不変性・同期など)
  • セキュリティ(入力チェック、認証など)

使い方

サンプルクラス

全体処理は以下のようにする。

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
PostSharpインストー

NuGetからPostSharpをインストールする。なお、ビルド時に設定ウィンドウが現れるので、Expressを使う。

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ってのがあるらしいが、上手く動かなかったため後日再チャレンジ予定。