Summary
Given a run-time reference to a type (and a value of that type), how can I create a completed Task
of that type with the given value?
Similar to Task.FromResult<T>(value)
, but where T
is unknown at compile time?
Background
Not that it matters, I'm trying to create a mock implementation of IAsyncQueryProvider
, by adapting some code I found here (including some changes for .NET Core 3.1 suggested in the comments by @Mandelbrotter).
Here's the relevant method:
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
var value = Execute(expression);
return Task.FromResult((dynamic) value);
}
Note that because this is an async method, TResult
is actually always of type Task<something>
. But, we can't declare the method like that because we're implementing a pre-defined interface. Nonetheless, we use Task.FromResult()
to return a completed Task
.
You'll notice that we don't use Task.FromResult(value)
directly; we cast value
to dynamic
first. This is because the return value of the Execute
call is declared as Object
, and if we simply did Task.FromResult(value)
we'd return something of type Task<Object>
, which is generally not the correct type - and that causes issues further up the stack.
However, while this approach works for queries returning simple types such as int
or DateTime
, it fails when the query returns a nullable value. In this case, when the value returned is not null the type of value
is just the underlying type, not the nullable type - and so we end up returning something of type Task<int>
instead of Task<int?>
.
I'd like to solve this (and do away with the use of dynamic
) by explicitly creating a Task
of the correct type. I can determine the desired type, either using reflection on TResult
, or more conveniently by using expression.Type
.
However, even once I know the type, I can't find a way to create a completed Task
of that type. Task.FromResult<T>
requires me to know T
at compile-time. I've not been able to find a way to create a completed Task
by passing in the type as a parameter.