<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/performance-best-practices-in-c--givbopafn" -->

---
title: Performance best practices in C# | daily.dev
description: A curated, non-exhaustive list of C#/.NET code patterns to avoid for performance and correctness reasons, compiled from real production issues observed at...
canonical: https://daily.dev/posts/performance-best-practices-in-c--givbopafn
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Performance best practices in C# | daily.dev
og:description: A curated, non-exhaustive list of C#/.NET code patterns to avoid for performance and correctness reasons, compiled from real production issues observed at...
og:url: https://daily.dev/posts/performance-best-practices-in-c--givbopafn
og:image: https://api.daily.dev/og/posts/GIvBoPAFn.png
og:image:alt: Performance best practices in C#
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Performance best practices in C#

**[Kevin Gosse \(minidump.net\)](https://daily.dev/sources/minidump)** · 10 min read · 0 upvotes · 0 comments

## Summary

A curated, non-exhaustive list of C#/.NET code patterns to avoid for performance and correctness reasons, compiled from real production issues observed at Criteo. Covers synchronous waits on async code causing threadpool starvation, correct use of ConfigureAwait, dangers of async void, avoiding unnecessary async state machines, ordinal vs culture-sensitive string comparisons, pitfalls of ConcurrentBag and ReaderWriterLockSlim, lambda vs method group allocations, costly enum-to-string and HasFlag boxing, struct equality members, boxing when structs implement interfaces, inlined CancellationToken and TaskCompletionSource continuations causing deadlocks, and when to prefer Task.Run over Task.Factory.StartNew.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://minidump.net/performance-best-practices-in-c-b85a47bdd93a>

## Questions this post answers

### Why does TaskCompletionSource cause a deadlock when SetResult is called from a background thread?

Continuations awaiting a TaskCompletionSource's task are often inlined and executed synchronously on the thread that calls SetResult. If that same thread later needs to wait on a signal the continuation depends on, it deadlocks. Passing TaskCreationOptions.RunContinuationsAsynchronously to the TaskCompletionSource constructor forces continuations to run asynchronously and avoids this. Note TaskContinuationOptions.RunContinuationsAsynchronously looks similar but is silently ignored.

_Developers debugging async deadlocks in .NET can find similar real-world gotchas curated on daily.dev._

### Why does Task.Factory.StartNew with an async method run out of order compared to Task.Run?

Task.Factory.StartNew returns a Task<Task> when given an async delegate, so awaiting only the outer task does not wait for the inner async work to complete, causing code after it to run before the async method finishes. Task.Run automatically unwraps the nested task and awaits the actual completion, making it the safer default unless a specific scheduler, long-running thread, or fairness queuing behavior is needed.

_Teams standardizing on safe task-scheduling patterns in C# can track these nuances via daily.dev._

### Is it faster to use Enum.HasFlag or a bitwise AND check for enum flags in C#?

A bitwise AND comparison, like (_option & Options.Option2) == Options.Option2, is significantly cheaper than Enum.HasFlag because HasFlag causes two boxing allocations: one converting the enum value to Enum and another for the virtual HasFlag call on a struct. As of .NET Core 2.1 the JIT optimizes plain enum equality comparisons automatically, but HasFlag itself remains costly.

_Developers optimizing hot paths in .NET services can keep up with these micro-optimizations through daily.dev._

## Similar posts on daily.dev

- [15 Advanced C\# Mistakes You’re Probably Still Making](https://daily.dev/posts/15-advanced-c-mistakes-you-re-probably-still-making-ctxsuyhsf) · ITNEXT · 36 upvotes · 2 comments
- [The Blueprint for High-Performance, Resilient .NET Applications](https://daily.dev/posts/the-blueprint-for-high-performance-resilient-net-applications-gr1arhboi) · C\# Corner · 0 upvotes · 0 comments
- [Async or Swim: The Complete Guide to Asynchronous .NET](https://daily.dev/posts/async-or-swim-the-complete-guide-to-asynchronous-net-n2fszfnf6) · ITNEXT · 9 upvotes · 1 comments

---

Tags: [#performance](https://daily.dev/tags/performance), [#.net](https://daily.dev/tags/.net), [#c#](https://daily.dev/tags/c#)

[View this post on daily.dev](https://daily.dev/posts/performance-best-practices-in-c--givbopafn)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Performance best practices in C#","url":"https://daily.dev/posts/performance-best-practices-in-c--givbopafn","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/performance-best-practices-in-c--givbopafn"},"datePublished":"2026-10-04T13:06:20.787Z","dateModified":"2026-10-04T13:10:08.021Z","description":"A curated, non-exhaustive list of C#/.NET code patterns to avoid for performance and correctness reasons, compiled from real production issues observed at...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/1056987b22c10d2dfbafaf2334787e56?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/1056987b22c10d2dfbafaf2334787e56?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Kevin Gosse (minidump.net)","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Kevin Gosse (minidump.net)","logo":"https://media.daily.dev/image/upload/s--8KHn1aFB--/f_auto,q_auto/v1791119046/logos/minidump?_a=BAMAMicg0","url":"https://daily.dev/sources/minidump"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/performance-best-practices-in-c--givbopafn","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"performance,.net,c#","timeRequired":"PT10M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Kevin Gosse (minidump.net)","item":"https://daily.dev/sources/minidump"},{"@type":"ListItem","position":3,"name":"Performance best practices in C#"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/performance-best-practices-in-c--givbopafn#faq","mainEntity":[{"@type":"Question","name":"Why does TaskCompletionSource cause a deadlock when SetResult is called from a background thread?","acceptedAnswer":{"@type":"Answer","text":"Continuations awaiting a TaskCompletionSource's task are often inlined and executed synchronously on the thread that calls SetResult. If that same thread later needs to wait on a signal the continuation depends on, it deadlocks. Passing TaskCreationOptions.RunContinuationsAsynchronously to the TaskCompletionSource constructor forces continuations to run asynchronously and avoids this. Note TaskContinuationOptions.RunContinuationsAsynchronously looks similar but is silently ignored. Developers debugging async deadlocks in .NET can find similar real-world gotchas curated on daily.dev."}},{"@type":"Question","name":"Why does Task.Factory.StartNew with an async method run out of order compared to Task.Run?","acceptedAnswer":{"@type":"Answer","text":"Task.Factory.StartNew returns a Task<Task> when given an async delegate, so awaiting only the outer task does not wait for the inner async work to complete, causing code after it to run before the async method finishes. Task.Run automatically unwraps the nested task and awaits the actual completion, making it the safer default unless a specific scheduler, long-running thread, or fairness queuing behavior is needed. Teams standardizing on safe task-scheduling patterns in C# can track these nuances via daily.dev."}},{"@type":"Question","name":"Is it faster to use Enum.HasFlag or a bitwise AND check for enum flags in C#?","acceptedAnswer":{"@type":"Answer","text":"A bitwise AND comparison, like (_option & Options.Option2) == Options.Option2, is significantly cheaper than Enum.HasFlag because HasFlag causes two boxing allocations: one converting the enum value to Enum and another for the virtual HasFlag call on a struct. As of .NET Core 2.1 the JIT optimizes plain enum equality comparisons automatically, but HasFlag itself remains costly. Developers optimizing hot paths in .NET services can keep up with these micro-optimizations through daily.dev."}}]}
```

