Damien_The_Unbeliever threw an interesting challenge. I think checking for AsyncStateMachineAttribute isn't a sufficient solution. The original question shouldn't be whether the method is async. Instead it should be whether it's awaitable. Both method samples in Damien's answer will return true if you check for the method GetAwaiter() on the return type. However, only the method marked async will include the AsyncStateMachineAttribute in the custom attributes collection.
Knowing if the method is awaitable is important if you want to use MethodInfo.Invoke() to call the method and you don't know ahead of time if methods that might be registered to a message broker are awaitable.
var isAwaitable = _methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
object result = null;
if (isAwaitable)
{
result = await (dynamic)_methodInfo.Invoke(_instance, _parameterArray);
}
else
{
result = _methodInfo.Invoke(_instance, _parameterArray);
}
EDIT:
Good idea to check the return type on MethodInfo. This is my revised code.
var isAwaitable = _methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
object invokeResult = null;
if (isAwaitable)
{
if (_methodInfo.ReturnType.IsGenericType)
{
invokeResult = (object)await (dynamic)_methodInfo.Invoke(_instance, arguments);
}
else
{
await (Task)_methodInfo.Invoke(_instance, arguments);
}
}
else
{
if (_methodInfo.ReturnType == typeof(void))
{
_methodInfo.Invoke(_instance, arguments);
}
else
{
invokeResult = _methodInfo.Invoke(_instance, arguments);
}
}
asyncis an implementation detail of the method, and should be changeable without any consumers caring.asyncmethod isn't really "complete" until the task that it returns completes, why should you not also treat any method that returns aTaskas incomplete until the task completes? Consider also that a non-async method returningTaskmay do a few simple things itself and then defer the bulk of its work to anasyncinternal method, and just pass back to its caller theTaskcreated by that method - are you planning to try to detect such a situation?async, the other may choose to not do so. Both are fulfilling the same contract, so why should they be treated differently? And, 6 months later, either or both of those classes may have had their implementations changed and added or removed theasynckeyword - but it doesn't change the contractual behaviour that they're providing.