Technically, you don't have to use async
/await
; it's just a lot easier than doing it "by hand". Your implementations just have to return Task
s that are never null
and already started (if applicable).
The TAP document has some useful techniques and guidelines. One key type is TaskCompletionSource<TResult>
which you can use to wrap any asynchronous implementation (e.g., APM). Synchronous implementations can use Task.FromResult
(which is just a small wrapper around TaskCompletionSource
. If you have CPU work to do, you can use Task.Factory.StartNew
, which is closely related but not quite equivalent to Task.Run
. For more complex implementations, you would need continuations a la ContinueWith
.
In other words, all the .NET 4.0 TPL techniques can be used, as long as your returned Task
s are always started. (The only way to get an unstarted Task
is to construct it with the Task
constructor and not call Start
. So as long as you don't do this, you should be fine.)