The problem
I have the following code:
Action function = async () => await Task.CompletedTask;
The problem with this code is that even though technically speaking it is an async delegate (one that can be awaited
) I cannot await it. In other words, the above could be written equivalently as
Func<Task> function = async () => await Task.CompletedTask;
The latter can be awaited, while the former cannot, even though this is the same method internally. Obviously, this is a contrived example, in my use case more complex code resides in a more complex codebase and it is not easy to spot all misusages.
The question
Can I check the actual type of function
at runtime and cast it to the correct one?
What I tried
Code below only throws if I declare function
as Func<Task>
(aka does not work as I expect it to)
Action function = async () => await Task.CompletedTask;
if (function.GetMethodInfo().ReturnType.IsEquivalentTo(typeof(Task)))
{
throw new Exception();
}
Code below does not even compile
Action function = async () => await Task.CompletedTask;
if (function is Func<Task> func)
{
}
CodePudding user response:
even though this is the same method internally.
It's not, though.
The delegate is compiled into a method, sure, but the type of that method is determined by what it's assigned to. If it's assigned to Func<Task>
, then it's a Task
-returning method. If it's assigned to Action
, then it's a void
-returning method.
So, check the actual type of function at runtime
isn't going to work; runtime is too late.
Instead, you need a compile-time check. And good news: there are analyzers that should catch this. E.g., ReSharper or Roslyn analyzers.
CodePudding user response:
Checking the type of the delegate at runtime
Does calling .GetType()
help?
Code below does not even compile
Action function = async () => await Task.CompletedTask; if (function is Func<Task> func) { }
This will compile and run
Action function = async () => await Task.CompletedTask;
if (function.GetType() == typeof(Func<Task>))
{
// not reachable
}
At runtime
Action function1 = async () => await Task.CompletedTask;
Func<Task> function2 = async () => await Task.CompletedTask;
var b = function1.GetType() == typeof(Action); // true
b = function1.GetType() == typeof(Func<Task>); // false
b = function2.GetType() == typeof(Action); // false
b = function2.GetType() == typeof(Func<Task>); // true
Some Useful Extensions
public static bool IsAction(this MulticastDelegate @delegate) =>
@delegate.GetType() == typeof(Action);
public static bool ReturnsTask(this MulticastDelegate @delegate) =>
@delegate.GetType() == typeof(Func<Task>);
public static bool ReturnsTask<T>(this MulticastDelegate @delegate) =>
@delegate.GetType() == typeof(Func<Task<T>>);
Example Usage
var function = async () => await Task.FromResult(true);
var b = function.IsAction(); // false
b = function.ReturnsTask(); // false
b = function.ReturnsTask<string>(); // false
b = function.ReturnsTask<bool>(); // true
if (b)
{
// possible to await
await function?.Invoke();
}