I have a simple interface
public interface SomethingProvider
{
public Something GetSomething();
}
To "make" it asynchronous, I would do this
public interface SomethingProvider
{
public Task<Something> GetSomethingAsync();
}
Although the interface now hints that GetSomething
is asynchronous, it allows synchronous execution, which is fine if the synchronous result is sufficiently fast. If it blocks, then I can assign the blame to poor implementation of the interface by the implementing programmer.
So if the latter interface is implemented by a sufficiently fast blocking implementation, then the latter interface is more flexible than the former and thus preferable.
In that case, should I rewrite all my interfaces to return tasks if there is the slightest chance that a call to a method will take more than some time?
EDIT:
I would like to emphasize that this is not a question about what Tasks are or how they work, but instead a design question relating to the inherent unknown implementation of interfaces when defining them. When writing an interface, it appears beneficial to allow for synchronous and asynchronous implementation alike.
A third "solution" could be some amalgamation of the two previous:
public interface SomethingProvider
{
public Something GetSomething();
public Task<Something> GetSomethingAsync();
}
but this breaks the Liskov substitution principle and adds nothing but confusion to the implementer.
Note: You also can use interface names as return types. In this case, the object returned must implement the specified interface.
The recommended return type of an asynchronous method in C# is Task. You should return Task<T> if you would like to write an asynchronous method that returns a value. If you would like to write an event handler, you can return void instead. Until C# 7.0 an asynchronous method could return Task, Task<T>, or void.
Yes, you can return an interface.
Yes, it is mandatory to implement all the methods in a class that implements an interface until and unless that class is declared as an abstract class. Implement every method defined by the interface.
This design issue is parallel with IDisposable
. When you are writing an interface, you have to know whether it's "likely" that derived types will need IDisposable
, and derive from it if they will. Non-disposable types like test stubs just implement a noop Dispose
.
Similarly, when you are writing an interface, you have to know whether it's "likely" that derived types will use asynchronous implementations.
Note that the question for derived types is: "Will the implementation be naturally asynchronous?", not "Will the implementation be fast?". Speed has nothing to do with it. The only thing you should consider is whether the implementations may be naturally-asynchronous (i.e., using I/O or other asynchronous methods).
When you write an interface, there's usually one (or a small number) of implementations that you have in mind; I recommend that you just consider those when deciding to make the interface methods asynchronous. You could go extreme and just make every interface method asynchronous, but that would be like using IDisposable
everywhere - not a pattern that I'd recommend.
So, I would say that if your (current) implementations are all synchronous for a particular method, then make that method synchronous; otherwise, make it asynchronous.
As you noted, an asynchronous method signature technically means that the implementation may be asynchronous. Test stubs and the like can use Task.FromResult
to implement an asynchronous signature synchronously. Personally, I think this is perfectly acceptable for stub implementations; but I would not recommend making a method async "just in case", when all current implementations are synchronous.
Also, I'm strongly against having both signatures. This would require implementors to wrap sync-over-async or async-over-sync, neither of which is ideal.
It depends on how "fast" you expect the interface method to be. For example, if the implementation is simple, something like:
public Something GetSomething(){
var s = new Something();
if(...)s.SomeField = 1;
return s;
}
Then returning a Task<>
method will incur more overhead than performance gain. It is very likely method does not have considerable performance impact at all.
But, if it does something like querying DB, url, or very complex calculations, e.g.:
public Something GetSomething(){
Something s = Cache.Get("some key");
if(s!=null)return s;
var con = new SqlConnection(...);
...;
return s;
}
Then you'd want to mark it Task<>
.
So in short, it's rather a design issue to be applied on a case to case basis, there is no absolute rule to follow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With