Custom Task-like Types (Extending async in C#)
Task is the default, but it’s not the only way to be awaitable. This post explains how C#‘s “awaitable pattern” lets you build custom task-like types for performance, control, and domain-specific async behavior.
What you’ll learn
- Why you’d want a custom awaitable: avoiding heap allocations, controlling scheduling/thread context, and integrating with other platforms
- The exact awaitable pattern —
GetAwaiter(),INotifyCompletion,IsCompleted,GetResult(), andOnCompleted(Action)— with no inheritance required - Building a minimal
MyAwaitable/awaiter step by step, and howIsCompleted,OnCompleted, andGetResultinteract - How
ValueTask<T>and struct-based awaitables reduce GC pressure in high-throughput code - Real-world use cases (game engines, high-performance servers, embedded/IoT, testing) and the caveats around debugging and complexity
Worth a read if you want to level up your understanding of how async/await really works under the hood.