最近由于一些业务上的需求,需要在 OnActionExcutionAsync 和 OnActionExcuted 中判断当前请求的接口是否是异步的接口,刚好前几天看过吕毅大佬 的文章《.NET 中什么样的类是可使用 await 异步等待的? 》,遂封装实现一下判断类型是否为可等待类型的方法。
理论 总结起来,要想使一个方法可被 await
等待,必须具备以下条件:
这个方法返回一个类 A 的实例,这个类 A 必须满足后面的条件。 此类 A 有一个可被访问到的 GetAwaiter
方法(扩展方法也行,这算是黑科技吗?),方法返回类 B 的实例,这个类 B 必须满足后面的条件; 此类 B 实现 INotifyCompletion
接口,且拥有 bool IsCompleted { get; }
属性、GetResult()
方法、void OnCompleted(Action continuation)
方法。 第三点中,OnCompleted
方法本身就是 INotifyCompletion
接口要求实现的,所以只需要校验前三点即可。
实践 按照要求封装以下扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using System.Linq;using System.Reflection;namespace System { public static class ReflectionExtension { public static bool IsAsyncType (this Type type ) { var awaiter = type.GetMethod("GetAwaiter" ); if (awaiter == null ) return false ; var retType = awaiter.ReturnType; if (retType.GetInterfaces().All(i => i.Name != "INotifyCompletion" )) return false ; if (retType.GetProperty("IsCompleted" ) == null ) return false ; if (retType.GetMethod("GetResult" ) == null ) return false ; return true ; } } }
借用大佬博客中的测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Program { static void Main (string [] args ) { Console.WriteLine(typeof (Test).IsAsyncType()); Console.ReadLine(); } } public class Test { public Test2 GetAwaiter () { return new Test2(); } } public class Test2 : INotifyCompletion { public bool IsCompleted { get ; } public void GetResult () { } public void OnCompleted (Action continuation ) { } }
在 OnActionExcutionAsync
和 OnActionExcuted
中的使用 在 OnActionExcutionAsync
和 OnActionExcuted
中使用时,可以再封装一层扩展方法,方便直接使用事件中的上下文实例调用。
1 2 public static bool IsAsyncAction (this FilterContext context ) => ((ControllerActionDescriptor) context.ActionDescriptor).MethodInfo.ReturnType.IsAsyncType();
调用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public override void OnActionExecuted (ActionExecutedContext context ){ if (context.IsAsyncAction()) Console.WriteLine("Current action is support async." ); } public override void OnActionExecuting (ActionExecutingContext context ){ if (context.IsAsyncAction()) Console.WriteLine("Current action is support async." ); } public override Task OnActionExecutionAsync (ActionExecutingContext context, ActionExecutionDelegate next ){ if (context.IsAsyncAction()) Console.WriteLine("Current action is support async." ); return base .OnActionExecutionAsync(context, next); }
以上↑