15

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.

kasperhj
  • 10,052
  • 21
  • 63
  • 106
  • 2
    If something is inherently synchronous, this would be a bad idea. If by "make" it asynchronous you simply invoke the already-synchronous code within something like `Task.Run`, that's also not a great idea. While that gives you something "asynchronous" your users will not be able to tell if the asynchronousity of it gives them any scalability. And in fact, it would force extra overhead on them that is not implied by "*Async". If your users needed to run that method asynchronously, it should be up to them to use `Task.Run` See: http://blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx – Peter Ritchie Jun 15 '14 at 18:21
  • 1
    Related: ["Generic constraint based on non-implementation of interface"](http://stackoverflow.com/questions/23016286/generic-constraint-based-on-non-implementation-of-interface). – noseratio Jun 16 '14 at 03:57
  • I agree. However, by the nature of the interface, there is no telling if the implementation will be synchronous or not. – kasperhj Jun 16 '14 at 07:09

3 Answers3

10

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.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
4

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.

kevin
  • 2,196
  • 1
  • 20
  • 24
  • I'm inclined to agree, but suppose you're writing the `SomethingProvider` interface, and you don't know how people will implement it. Wouldn't it be more flexible to write it as a Task returning method? Thus if somebody down the line write `RemoteSomethingProvider` it is already supported. – kasperhj Jun 16 '14 at 07:18
  • 1
    Counter argument - anyone can turn a blocking method into async by simply using `new Task(()=>...);` or `new Thread(()=>...);`. On the other hand any `Task` can be turned into a blocking method by calling `DoSomethingAsync().Result;`. There is no right or wrong, but rather, better or worse depending on context. – kevin Jun 16 '14 at 16:10
4

When you create an interface with Task<> you're telling the implementers of your interface: "This method will run asynchronously. It will trigger work which will finish sometime in the future". It isn't about how "fast" the method will run, an async method may return quickly from a web request. It is true that await on an async operation will trigger the overhead of the state machine, but it does definitely not guarantee the "speed" of execution.

You shouldn't wrap all your methods with a Task based on how fast they execute, You should mark them so if they represent code that is truely pure asynchronous.

I like the definition of Task from What is the difference between task and thread? (with a slight modification thanks to @akshay2000)

In computer science terms, a Task is a future or a promise. (Some people use those two terms synomymously, some use them differently, nobody can agree on a precise definition.) Basically, a Task "promises" to return you a T, but not right now honey, ill get back to you when i'm ready

Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 3
    `why don't you come back later?` I think the concept works more like `I'll get back to you when I'm ready.` Isn't this one more appropriate? – akshay2000 Jun 15 '14 at 18:09
  • Its meant to express the fact that a `T` wont return immediately but sometime in the future. I wont argue on the exact definition since i didnt make it :) – Yuval Itzchakov Jun 15 '14 at 18:16
  • Creating an interface with a method return a `Task<>` does _not_ run anything asynchronously. The question here is not "What's a Task", but if interfaces should, in general, be written with asynchrony in mind. – kasperhj Jun 16 '14 at 07:07
  • I didn't say that making a method return `Task<>` runs anything asynchronously. I said, as long as there is no asynchronous process in progress inside that method, there is no reason to mark it as a `Task<>`. You cant force to make every process asynchronous, or even make it look like it is one. That will force whoever is implementing your interface to make workarounds to return a `Task` – Yuval Itzchakov Jun 16 '14 at 07:24