C# Application Performance Optimization: .NET Tips and Practices

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why C# Performance Matters

C# and the .NET platform are powerful tools for building high-load web services, desktop applications, and Unity games. However, even the most elegant code can run slowly if you don't account for the specifics of how the CLR (Common Language Runtime), Garbage Collector (GC), and JIT compilation mechanisms work.

In this article, we will break down key methods for optimizing the performance of C# applications that will help you reduce response time, decrease memory consumption, and increase system throughput. All tips are accompanied by practical code examples.



1. Memory Management and Garbage Collection (GC)

One of the main features of .NET is automatic memory management. But improper use of objects leads to frequent garbage collections, which causes application "pauses" (freezes).



1.1. Avoid Allocations in Hot Paths

Try not to create new objects inside loops or frequently called methods. Instead, use object pools or reuse instances.

// Bad: a new StringBuilder is created on each iterationfor (int i = 0; i < 1000; i++){    var sb = new StringBuilder();    sb.Append(i);}

// Good: use a pool or reusevar sbPool = new StringBuilder();for (int i = 0; i < 1000; i++){ sbPool.Clear(); sbPool.Append(i);}


1.2. Use Structs Instead of Classes for Small Data

Structs are stored on the stack and do not create load on the GC. But be careful: large structs (more than 16 bytes) can be slow to copy.

public struct Point{    public int X;    public int Y;}

// Now calling the method does not create an object on the heapvoid ProcessPoint(Point p) { /* ... */ }


1.3. Enable Server GC Mode for Server Applications

By default, console applications use Workstation GC. For web services (ASP.NET Core), switch to Server GC in the csproj file:

<PropertyGroup>    <ServerGarbageCollection>true</ServerGarbageCollection></PropertyGroup>


2. Optimizing Work with LINQ and Collections

LINQ is a convenient but potentially slow tool if you don't know its "pitfalls".



2.1. Avoid Multiple Enumerations

Each call to .Count(), .ToList(), or .Any() can re-execute the query. Save the result to a variable.

// Bad: two passes over the collectionif (items.Where(x => x.IsActive).Count() > 0) { /* ... */ }var activeList = items.Where(x => x.IsActive).ToList();

// Good: one passvar activeItems = items.Where(x => x.IsActive).ToList();if (activeItems.Count > 0) { /* ... */ }


2.2. Use Specialized Collections

For frequent key-based lookups, use Dictionary<TKey, TValue> instead of List<T>. For "first in, first out" operations, use Queue<T>.

// Fast lookup: O(1) instead of O(n)var dict = new Dictionary<int, string>();dict.Add(1, "one");if (dict.ContainsKey(1)) { /* ... */ }


2.3. Replace LINQ with Loops in Critical Sections

For simple operations (sum, filtering), a for or foreach loop can be 2-3 times faster than LINQ.

// LINQ (slow)int sum = numbers.Sum();

// Loop (fast)int sum = 0;for (int i = 0; i < numbers.Length; i++){ sum += numbers[i];}


3. Asynchronous Programming and Reducing Blocking

Proper use of async/await allows you to not block threads, which is critical for scalability.



3.1. Use ValueTask for Frequently Synchronous Results

Blogs

Book Recommendations