簡単な紹介
狭い円では、PostSharpやFodyなどのアスペクト指向プログラミングツールが広く知られています。
最初のものはシェアウェアユーティリティであり、私の意見では、その無料版では非常に制限されています。特に、Windowsストアプロジェクトには適用できず、10を超えるクラスで自動INotifyPropertyChangedを使用します。 これらの制限の多くと比較的高い価格により、代替手段を検討しています。
一方、FodyはMono.Cecilに基づいて無料であり、多くのプラグインが付属しています。 AlexeySuvorovによるこの記事でそれらの詳細を読むことができます。 これらのプラグインの1つであるMethodDecoratorは、以前の記事の著者でもありますが、筆者はロギングの実装中に遭遇しました。
だから、装飾方法
MethodDecoratorExパッケージを読み込んでロギング(またはその他の種類の処理)を簡素化した後、IMethodDecoratorインターフェイスを入力、出力、および例外処理メソッドとともに継承する必要な属性が作成され、必要なメソッドにハングアップします。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)] public class AsyncInterceptorAttribute : Attribute, IMethodDecorator { public void Init(object instance, MethodBase methodBase, object[] args) { ... } public void OnEntry() { ... } public void OnExit() { ... } public void OnException(Exception exception) { ... } }
コンパイル後にそのような属性で装飾されたメソッドには、メソッドへの入力を処理し、終了し、例外をキャッチするためのコードが含まれています。
[AsyncInterceptor] public string Bar() { return "Hi"; }
ただし、メソッドが非同期の場合、問題が発生します。 メソッドの入力とメソッドの終了は問題なくインターセプトされますが、例外が発生した場合、まったく記録されません。 これは、属性で装飾された非同期メソッドでさえ、特別な有限状態の魔法に変換され、メソッドで例外をキャッチできないために発生します。
[AsyncInterceptor] public async Task<string> Bar() { throw new Exception(); }
この問題を解決するために、次の回避策が使用されました-MethodDecoratorExを変更して、返されたタスクをインターセプトし、次のようにTaskContinuationメソッドで処理できるようにしました。
public void TaskContinuation(Task task) { task.ContinueWith(OnTaskFaulted, TaskContinuationOptions.OnlyOnFaulted); task.ContinueWith(OnTaskCancelled, TaskContinuationOptions.OnlyOnCanceled); task.ContinueWith(OnTaskCompleted, TaskContinuationOptions.OnlyOnRanToCompletion); } private void OnTaskFaulted(Task t) { ... } private void OnTaskCancelled(Task t) { ... } private void OnTaskCompleted(Task t) { ... }
MethodDecoratorExプロジェクトの変更点は何ですか?
非常に少ない。 TaskContinuationメソッドの受信が追加され、戻り値にTask型の名前が含まれていることを確認しました。 これに応じて、3つのIL命令の実行が追加されます。
private static IEnumerable<Instruction> GetTaskContinuationInstructions( ILProcessor processor, VariableDefinition retvalVariableDefinition, VariableDefinition attributeVariableDefinition, MethodReference taskContinuationMethodReference) { if (retvalVariableDefinition == null) return new Instruction[0]; var tr = retvalVariableDefinition.VariableType; if (tr.FullName.Contains("Task")) { return new[] { processor.Create(OpCodes.Ldloc_S, attributeVariableDefinition), processor.Create(OpCodes.Ldloc_S, retvalVariableDefinition), processor.Create(OpCodes.Callvirt, taskContinuationMethodReference), }; } return new Instruction[0]; }
この実装は私のタスクで正しく動作しますが、いくつかの欠点があります。 それでも、膝の上で修正されたプロジェクトはまだ少し湿っています。
特に、MethodDecoratorEx用に作成されたすべてのxUnitテストが突然クラッシュします。 これに対処する時間がないので、誰かがテストを正しい方法で書き直したい、または助けたいと思っているなら、私はうれしいです。
タスクのチェックを少し改善することもできます。
ここでプロジェクト
NuGetパッケージはこちら
ご静聴ありがとうございました。