I want to implement an interface that has a Run method:
public interface IRunner
{
bool Run();
}
But the issue I am facing is that some Run functions should be synchronous and some should be asynchronous. One solution would be to implement two interfaces:
public interface IRunner
{
bool Run();
}
public interface IRunnerAsync
{
Task<bool> RunAsync();
}
But the issue here is, that if I have, say, a list of objects implementing these interfaces, I will have to always make a check of what type each object is before calling the method, which feels hacky. What would be best practices to implement interfaces like these?
CodePudding user response:
Perhaps just have the async version, and accept that some callers may implement it internally in a synchronous fashion; they can do this locally and just return the true
/false
as a wrapped task via Task.FromResult
, noting that (at least in current frameworks) the runtime will special-case the bool
case, and avoid allocating multiple tasks internally (the details aren't important). Your consumption code can just await
an already-completed synchronous result, and everything will keep working. In some very specific scenarios (usually in library code), it may be beneficial for your consumption code to explicitly test for synchronously-completed results, and optimize away some of the async
overheads.
In a more general Task<T>
scenario, where you don't want the synchronous implementations to have to allocate, consider switching to ValueTask<T>
instead; with that API, synchronous implementations can return a new ValueTask<TheType>(value)
without allocating, and asynchronous versions can either wrap a Task<T>
, or can simply declare themselves as async ValueTask<T> Whatever()
and let the compiler worry about the details. There is also a third way of implementing ValueTask<T>
, for niche async low-allocation scenarios, but: that's probably overkill here. One caveat with ValueTask[<T>]
: a value-task should only be awaited once (or rather: the outcome should only be retrieved once).