PostSharpのいくつかの便利な側面

.netにはいくつかの本格的なAOPフレームワークがありますが、 PostSharp以降、それらの1つは「操縦」されません。 このフレームワークの大ファン(およびユーザー)として、コミュニティにいくつかの「レシピ」を紹介したいと思います。 私はそれらのいくつかを自分で作成し、他のものはインターネットで見つけて、私のニーズに適合させました。 ここでは、「最もジューシーな」レシピをいくつか紹介します。 また、AOPフレームワークやイデオロギーに慣れていない場合は、 こちらのWebキャストをお勧めします 。 それでは、始めましょうか?





ストリーム



PostSharpで最も「ステレオタイプ」な側面は、プール内のタグ付きメソッドを呼び出すための側面です。 アスペクトは非常にシンプルですが、時には非常に便利です。







public class WorkerThreadAttribute : OnMethodInvocationAspect<br/>

{<br/>

public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>

{<br/>

ThreadPool.QueueUserWorkItem(state => eventArgs.Proceed());<br/>

}<br/>

}<br/>





興味深いのは、 QueueUserWorkItem



直接使用する場合のように、このようなコードを呼び出して、型変換なしで戻り値を取得できることです。



メソッドを特定のスレッドに「ダイレクト」する機会があるため、WinFormsのように、メソッドを宣言的にUIスレッドで実行することを簡単に宣言できます。



public class FormsThreadAttribute : OnMethodInvocationAspect<br/>

{<br/>

public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>

{<br/>

Form f = (Form)eventArgs.Instance;<br/>

if (f.InvokeRequired)<br/>

f.Invoke(eventArgs.Delegate, eventArgs.GetArgumentArray());<br/>

else <br/>

eventArgs.Proceed();<br/>

}<br/>

}<br/>





...およびWPFの場合:



public class WpfThreadAttribute : OnMethodInvocationAspect<br/>

{<br/>

private DispatcherPriority priority;<br/>

public WpfThreadAttribute() : this (DispatcherPriority.Normal) { }<br/>

public WpfThreadAttribute(DispatcherPriority priority)<br/>

{<br/>

this .priority = priority;<br/>

}<br/>

public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>

{<br/>

DispatcherObject dispatcherObject = (DispatcherObject)eventArgs.Instance;<br/>

if (dispatcherObject.CheckAccess())<br/>

eventArgs.Proceed();<br/>

else <br/>

dispatcherObject.Dispatcher.Invoke(priority, new Action(eventArgs.Proceed));<br/>

}<br/>

}<br/>





後者の場合、実行を優先する機会さえあります。



プロパティの書き換え



ほとんどの場合、 set



/ get



メソッド内のロジックはドメイン固有ですが、「作成」するのに役立つことがある1つの側面が変更通知設定です。これは、プロパティ変更通知と呼ばれます。 WinFormsにはINotifyPropertyChanged



インターフェイスがありますが、これは手動で実装するのがINotifyPropertyChanged



場合があります。 このインターフェイスはWPFでも機能しますが、別の代替手段があります-いわゆる作成です。 依存関係プロパティ。



両方のソリューションは、通常のCLR自動プロパティに自動的に適用できます。 どちらの場合も、ソリューションは非常に複雑なので、コードの代わりにINotifyPropertyChangedおよびDependency Propertiesの実装へのリンクを投稿します 。 ちなみに、私はこれらのスニペットの最初のものを私が長い間使っていたと言います、そして私は「手動」の実装を使うことはほとんどありません-ネオフォビアがそれを強制しない限り。



遅延初期化



遅延の解決策のように思えるかもしれませんが、遅延初期化は宣言的に行うことができます。 さらに、PostSharpを使用すると、プロパティの値がIoCコンテナーから取得された場合でも、プロパティの遅延初期化を保存できます。 簡単な例を次に示します-その中で、独自の方法でフィールドを書き換えます:



[Serializable]<br/>

class LazyLoad : OnFieldAccessAspect<br/>

{<br/>

private readonly Type type;<br/>

private readonly object [] args;<br/>

public LazyLoad(Type type, params object [] arguments)<br/>

{<br/>

this .type = type;<br/>

args = arguments;<br/>

}<br/>

public override OnFieldAccessAspectOptions GetOptions()<br/>

{<br/>

return OnFieldAccessAspectOptions.RemoveFieldStorage;<br/>

}<br/>

public override void OnGetValue(FieldAccessEventArgs eventArgs)<br/>

{<br/>

if (eventArgs.StoredFieldValue == null )<br/>

eventArgs.StoredFieldValue = Activator.CreateInstance(type, args);<br/>

eventArgs.ExposedFieldValue = eventArgs.StoredFieldValue;<br/>

}<br/>

}<br/>





残念ながら、このコードには非常に適切な実装は含まれていません(オブジェクト型はコンストラクターに渡されます)が、一方で、このような実装は柔軟性を高めます。 そしてもちろん、 Activator.CreateInstance



代わりに、たとえば「すべて同時に」発行される従来のDIモデルをバイパスして、 My.IoCContainer.Resolve



Activator.CreateInstance



呼び出すことはありません。



試行



別の古典的なパターン(またはすべての種類の構文糖を嫌う人のためのアンチパターン)は、メソッドを呼び出すことができ、例外の場合、特定の時間間隔でメソッドをさらに数回呼び出そうとする側面です。



[Serializable]<br/>

public class RetryAttribute : OnMethodInvocationAspect<br/>

{<br/>

public int Count { get; set; }<br/>

public int DelayMsec { get; set; }<br/>

public RetryAttribute( int count) : this (count, 0) { }<br/>

public RetryAttribute( int count, int delayMsec)<br/>

{<br/>

Count = count;<br/>

DelayMsec = delayMsec;<br/>

}<br/>

public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>

{<br/>

for ( int a = 0; ; ++a)<br/>

{<br/>

try <br/>

{<br/>

if (a != 0 && DelayMsec > 0)<br/>

Thread.Sleep(DelayMsec);<br/>

eventArgs.Proceed();<br/>

return ;<br/>

} <br/>

catch <br/>

{<br/>

if (a == Count) throw ;<br/>

}<br/>

}<br/>

}<br/>

}<br/>





この属性を使用すると、システムの「バガリー」を管理できます。 また、その変更された形式は同じ関数を何度も受動的に呼び出し、すべての呼び出しの結果を集約できます。 私の実践では、このアプローチは、たとえば、ローカルネットワークで実行中のSQLサーバーを一覧表示する場合に役立ちました。もちろん、検索機能の最初の呼び出しでは、すべてではなくいくつかのサーバーが見つかります。



例外



例外処理は、宣言型にすることができる開発の別の側面です。 キャッチされていない例外をキャッチし、エラーのあるメッセージを発行するアスペクトの例を次に示します。



[Serializable]<br/>

public class ExceptionDialogAttribute : OnExceptionAspect<br/>

{<br/>

public override void OnException(MethodExecutionEventArgs eventArgs)<br/>

{<br/>

ExceptionMessageBox emb = new ExceptionMessageBox(<br/>

eventArgs.Exception,<br/>

ExceptionMessageBoxButtons.OK,<br/>

ExceptionMessageBoxSymbol.Error);<br/>

emb.Show(eventArgs.Instance as Form);<br/>

eventArgs.FlowBehavior = FlowBehavior.Continue;<br/>

}<br/>

}<br/>





通常のMessageBox



とは異なりMessageBox



私はより美しく有用なExceptionMessageBox



を使用しました。 原理は、原則として非常に単純です。 そしてもちろん、この側面は、特定のタイプの例外を一般的に無視するようにカスタマイズできます。 たとえば、Visual Studioアドインを初期化する場合、 ArgumentException



型の例外は一般的です。 スタジオは、既存のコマンドにメニュー項目を追加しようとすることがあります。 例外を「飲み込む」には、次の側面を使用できます。



[Serializable]<br/>

public class IgnoreExceptionAttribute : OnExceptionAspect<br/>

{<br/>

private Type type;<br/>

public IgnoreExceptionAttribute(Type type)<br/>

{<br/>

this .type = type;<br/>

}<br/>

public override Type GetExceptionType(System.Reflection.MethodBase method)<br/>

{<br/>

return type;<br/>

}<br/>

public override void OnException(MethodExecutionEventArgs eventArgs)<br/>

{<br/>

eventArgs.FlowBehavior = FlowBehavior.Continue;<br/>

} <br/>

}<br/>





ASP.NETでサイトを作成する場合、少し異なるアプローチがあります。例外が発生すると「安全な」ページを提供しますが、例外(ASP.NET MVCシステムが実際に処理する)をスローするときにインターセプトおよびセキュリティ保護されます。 このために、 CodePlex.Diagnosticsフレームワークとこの単純な側面を使用します。



[Serializable]<br/>

public sealed class InterceptExceptionAttribute : OnExceptionAspect<br/>

{<br/>

public override void OnException(MethodExecutionEventArgs eventArgs)<br/>

{<br/>

IIdentity identity = WindowsIdentity.GetCurrent();<br/>

Guid guid = ExceptionProvider.Publish(eventArgs.Exception, identity);<br/>

throw new PublishedException(guid, eventArgs.Exception);<br/>

}<br/>

}<br/>





トレースとロギング



ロギングについて話しているので、これらの目的のために側面を見せないことは罪です。 ほとんどの開発者はこの問題に対する独自の個別のアプローチを持っているという事実にもかかわらず、私はメソッドの入力と出力、および例外を記録する素晴らしい側面を示したいと思います。 これらすべての場合、関連情報はDebug.WriteLine



単純な呼び出しでスタジオの出力ウィンドウに表示されます:



[Serializable]<br/>

public sealed class LogAttribute : OnMethodBoundaryAspect<br/>

{<br/>

private static int indent;<br/>

private static int indentSize = 4;<br/>

private static string IndentString<br/>

{<br/>

get<br/>

{<br/>

return new string (Enumerable.Range(0, indentSize*indent).Select(n => n%4 == 0 ? '|' : ' ' ).ToArray());<br/>

}<br/>

}<br/>

private static void Indent() { ++indent; }<br/>

private static void Unindent() { if (indent > 0) --indent; }<br/>

private static void WriteLine( string s) { Debug.WriteLine(IndentString + s); }<br/>

public override void OnEntry(MethodExecutionEventArgs eventArgs)<br/>

{<br/>

StringBuilder sb = new StringBuilder();<br/>

sb.AppendFormat( "Entering {0} with arguments" , eventArgs.Method);<br/>

object [] args = eventArgs.GetReadOnlyArgumentArray() ?? new object [] {};<br/>

foreach (var arg in args)<br/>

{<br/>

sb.AppendFormat( " <{0}>" , arg);<br/>

}<br/>

WriteLine(sb.ToString());<br/>

Indent();<br/>

}<br/>

public override void OnExit(MethodExecutionEventArgs eventArgs)<br/>

{<br/>

Unindent();<br/>

WriteLine( "Exiting " + eventArgs.Method);<br/>

}<br/>

public override void OnException(MethodExecutionEventArgs eventArgs)<br/>

{<br/>

StringBuilder sb = new StringBuilder();<br/>

sb.AppendFormat( "Exception {0} in {1}" , eventArgs.Exception, eventArgs.Method);<br/>

sb.AppendLine();<br/>

WriteLine(sb.ToString());<br/>

}<br/>

}<br/>





ここで、ロギング用にlog4PostSharpアドオンがあり、 log4netサブシステムを使用するメソッドの入り口と出口に関する情報を記録することに注意してください



パフォーマンス測定



PostSharpの著者自身は、CodeProjectの記事の 1つで、メソッドの実行速度を計算したり、呼び出しの回数を記録したりするためのアスペクトの興味深い使用法を示しました。 これらの機能は、アスペクトでは、通常のコードと同様に、静的メンバーを持つことができ、いつでも呼び出すことができるという事実に基づいています。 次の側面を可能にするもの:



[Serializable]<br/>

public class PerformanceCounterAttribute : OnMethodInvocationAspect<br/>

{<br/>

private static readonly Dictionary< string , PerformanceCounterAttribute><br/>

attributes = new Dictionary< string , PerformanceCounterAttribute>();<br/>

private long elapsedTicks;<br/>

private long hits;<br/>

[NonSerialized]<br/>

private MethodBase method;<br/>

public MethodBase Method { get { return method; } }<br/>

public double ElapsedMilliseconds<br/>

{<br/>

get { return elapsedTicks / (Stopwatch.Frequency / 1000d); }<br/>

}<br/>

public long Hits { get { return hits; } }<br/>

public static IDictionary< string , PerformanceCounterAttribute> Attributes<br/>

{<br/>

get<br/>

{<br/>

return attributes;<br/>

}<br/>

}<br/>

public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>

{<br/>

Stopwatch stopwatch = Stopwatch.StartNew();<br/>

try <br/>

{<br/>

eventArgs.Proceed();<br/>

}<br/>

finally <br/>

{<br/>

stopwatch.Stop();<br/>

Interlocked.Add( ref elapsedTicks, stopwatch.ElapsedTicks);<br/>

Interlocked.Increment( ref hits);<br/>

}<br/>

}<br/>

public override void RuntimeInitialize(MethodBase method)<br/>

{<br/>

base .RuntimeInitialize(method);<br/>

this .method = method;<br/>

attributes.Add(method.Name, this );<br/>

}<br/>

}<br/>





このような興味深いPostSharp。 そして、あなたはどんな側面を知っていますか?



All Articles