As some of you may know, many of the features in C# light up through “duck typing”. Duck typing is where you accept an object that behaves in a certain way (i.e. has certain methods, properties, etc.) instead of a specific type, or as is usually explained: “If it walks like a duck, and talks like a duck, it’s a duck”.

The foreach statement for example, doesn’t look for types that implement IEnumerable / IEnumerable<T> but instead expects a GetEnumerator method that returns some enumerator type (can be either a class or a struct ) that has MoveNext and Current . So while the following code clearly breaks in runtime the compiler has no issues with it:

void Foo () { foreach ( var item in new FakeEnumerable ()) { Console . WriteLine ( item ); } } class FakeEnumerable { public FakeEnumerator GetEnumerator () => new FakeEnumerator (); } struct FakeEnumerator { public bool MoveNext () => throw new NotImplementedException (); public object Current => throw new NotImplementedException (); }

One of these features is async/await (or the Task-based Asynchronous Pattern). The compiler doesn’t expect Task or Task<T> specifically, it looks for a GetAwaiter method that returns an awaiter that in turn implements INotifyCompletion and has:

IsCompleted : Enables optimizations when the operation completes synchronously.

: Enables optimizations when the operation completes synchronously. OnCompleted : Accepts the callback to invoke when the asynchronous operation completes.

: Accepts the callback to invoke when the asynchronous operation completes. GetResult : Returns the result of the operation (if there is one) and rethrows the exception if one occurred.

GetAwaiter can also be an extension method so it’s possible to turn existing types to awaitables with the right custom awaiter.

I recently made such an awaiter for awaiting a collection of tasks together by calling Task.WhenAll and wrapping the returned Task ’s awaiter:

struct TaskEnumerableAwaiter : INotifyCompletion { private TaskAwaiter _awaiter ; public bool IsCompleted => _awaiter . IsCompleted ; internal TaskEnumerableAwaiter ( IEnumerable < Task > tasks ) => _awaiter = Task . WhenAll ( tasks ). GetAwaiter (); public void OnCompleted ( Action continuation ) => _awaiter . OnCompleted ( continuation ); public void GetResult () => _awaiter . GetResult (); } public static class EnumerableExtensions { public static TaskEnumerableAwaiter GetAwaiter ( this IEnumerable < Task > tasks ) => new TaskEnumerableAwaiter ( tasks ); }

The GetAwaiter extension method for IEnumerable<Task> returning this TaskEnumerableAwaiter means the compiler can treat a collection of tasks as an awaitable and await it just like any other task:

HttpClient _httpClient = new HttpClient (); async Task DownloadAllAsync () { var urls = new [] { "http://www.google.com" , "http://www.github.com" , "http://www.twitter.com" }; await urls . Select ( DownloadAsync ); } async Task DownloadAsync ( string url ) { var content = await _httpClient . GetStringAsync ( url ); Console . WriteLine ( content ); }

This may be confusing for developers who aren’t familiar enough with async/await so I can’t recommend this as a best-practice, but if you’re still interested I added this support to my AsyncUtilities library.