Asynchronous Programming in C#

Async/await patterns and “return await”

  • Disagreement over guidance to “prefer async/await over returning Task directly.”
    • Critics: adding async + await for a single call allocates an extra state machine and is unnecessary unless you truly need post-processing, IDisposable cleanup, AsyncLocal, or better stack traces.
    • Supporters of the critique liken redundant return await Task.FromResult(value) to an obvious anti-pattern.

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 void crashes the process vs. silently swallowing errors.
  • Comments note historical behavior and current patterns (event handlers, TaskScheduler.UnobservedTaskException, explicit logging).
  • General view: async void is mostly for events and should be wrapped with top-level try/catch.

Performance, concurrency patterns, and ContinueWith

  • Sequential await calls are slower than sync and don’t parallelize; starting Tasks first and awaiting later (or Task.WhenAll) is needed for concurrency.
  • ContinueWith is described as legacy/awkward compared to async/await; LINQ-style composition and WhenAll are 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.