非同期メソッドのFody MethodDecoratorExの改善

この記事では、非同期メソッドを装飾する機能を追加した、Fody.MethodDecoratorプロジェクトの小さな改善に焦点を当てます。



簡単な紹介



狭い円では、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パッケージはこちら



ご静聴ありがとうございました。



All Articles