Asynchronous Programming in C#
Async/await patterns and “return await”
- Disagreement over guidance to “prefer async/await over returning Task directly.”
- Critics: adding
async+awaitfor a single call allocates an extra state machine and is unnecessary unless you truly need post-processing,IDisposablecleanup,AsyncLocal, or better stack traces. - Supporters of the critique liken redundant
return await Task.FromResult(value)to an obvious anti-pattern.
- Critics: adding
Sync-over-async and “async all the way”
- Many object to the claim that “asynchrony is viral” and you should “make everything async at once.”
- Real-world code often has synchronous entry points that cannot feasibly be made async, yet must call async APIs.
- Various sync-over-async tricks (
Task.Result,.Wait(),GetAwaiter().GetResult(),Task.Run(...).Result) are discussed; all have deadlock or context risks. - Some argue the article intentionally avoids giving a recipe because it would be overused; others want at least a clearly marked “last resort” pattern.
- Consensus: best practice is async end‑to‑end when possible; blocking on Tasks is inherently fragile.
async void and exception handling
- Confusion around whether
async voidcrashes the process vs. silently swallowing errors. - Comments note historical behavior and current patterns (event handlers,
TaskScheduler.UnobservedTaskException, explicit logging). - General view:
async voidis mostly for events and should be wrapped with top-level try/catch.
Performance, concurrency patterns, and ContinueWith
- Sequential
awaitcalls are slower than sync and don’t parallelize; starting Tasks first and awaiting later (orTask.WhenAll) is needed for concurrency. ContinueWithis described as legacy/awkward compared to async/await; LINQ-style composition andWhenAllare preferred.
C# language, ecosystem, and tooling
- Many praise C# as pleasant, powerful, and thoughtfully designed; some like it more than Java or even prefer Dart/F#.
- Critiques:
- Async complexity is off‑putting, especially compared to Go’s goroutines.
- NuGet ecosystem seen by some as weaker than npm/pip, with many “enterprise” paid components; others strongly dispute this and say modern NuGet + OSS is fine.
- Cross‑platform gaps (e.g.,
System.Drawing/GDI, missing graphics/UI libraries) force third‑party libs. - Some dislike aspects inherited from early .NET (e.g.,
object-based equality, culture defaults) and lament missing/weak features (true non-nullable refs, stronger generics constraints).
Migration, legacy, and analyzers
- Large codebases struggle to “go async” or move from .NET Framework to .NET while also fixing sync/async boundaries.
- An analyzer package is highlighted as useful for catching common async mistakes.
- Some feel the article’s empty or brief sections (e.g.,
ConfigureAwait) and lack of migration guidance are notable gaps.