public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info) { if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo)) { info = new CoercedAwaitableInfo(directlyAwaitableInfo); return(true); } info = default(CoercedAwaitableInfo); return(false); }
public CoercedAwaitableInfo(AwaitableInfo awaitableInfo) { AwaitableInfo = awaitableInfo; CoercerExpression = null; CoercerResultType = null; }
public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo) { CoercerExpression = coercerExpression; CoercerResultType = coercerResultType; AwaitableInfo = coercedAwaitableInfo; }
public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo) { // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347 // Awaitable must have method matching "object GetAwaiter()" var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m => m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == 0 && m.ReturnType != null); if (getAwaiterMethod == null) { awaitableInfo = default(AwaitableInfo); return(false); } var awaiterType = getAwaiterMethod.ReturnType; // Awaiter must have property matching "bool IsCompleted { get; }" var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p => p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase) && p.PropertyType == typeof(bool) && p.GetMethod != null); if (isCompletedProperty == null) { awaitableInfo = default(AwaitableInfo); return(false); } // Awaiter must implement INotifyCompletion var awaiterInterfaces = awaiterType.GetInterfaces(); var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion)); if (!implementsINotifyCompletion) { awaitableInfo = default(AwaitableInfo); return(false); } // INotifyCompletion supplies a method matching "void OnCompleted(Action action)" var iNotifyCompletionMap = awaiterType .GetTypeInfo() .GetRuntimeInterfaceMap(typeof(INotifyCompletion)); var onCompletedMethod = iNotifyCompletionMap.InterfaceMethods.Single(m => m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase) && m.ReturnType == typeof(void) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Action)); // Awaiter optionally implements ICriticalNotifyCompletion var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion)); MethodInfo unsafeOnCompletedMethod; if (implementsICriticalNotifyCompletion) { // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)" var iCriticalNotifyCompletionMap = awaiterType .GetTypeInfo() .GetRuntimeInterfaceMap(typeof(ICriticalNotifyCompletion)); unsafeOnCompletedMethod = iCriticalNotifyCompletionMap.InterfaceMethods.Single(m => m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase) && m.ReturnType == typeof(void) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Action)); } else { unsafeOnCompletedMethod = null; } // Awaiter must have method matching "void GetResult" or "T GetResult()" var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m => m.Name.Equals("GetResult") && m.GetParameters().Length == 0); if (getResultMethod == null) { awaitableInfo = default(AwaitableInfo); return(false); } awaitableInfo = new AwaitableInfo( awaiterType, isCompletedProperty, getResultMethod, onCompletedMethod, unsafeOnCompletedMethod, getResultMethod.ReturnType, getAwaiterMethod); return(true); }