C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
142: Working with StringBuilder for Performance
One of the most common performance traps I see developers fall into—even experienced ones—is treating strings like they are mutable. In C#, strings are immutable. Every time you "change" a string, you aren't actually changing it; you're creating a brand new string object in memory and throwing the old one away. When you're doing this three or four times, it doesn't matter. When you're doing it inside a loop with a thousand iterations, you're creating a massive amount of garbage for the GC (Garbage Collector) to clean up, and your app will slow to a crawl.
Let's build a simple System Health Report generator. We have a list of server components, and we want to aggregate their statuses into one final report string.
The trap of the plus-equals operator
I'll start the way a lot of people do. I've got a list of status messages, and I just want to smash them together into one big block of text. Here is my first attempt:
var statuses = new List<string> { "CPU: OK", "RAM: Warning", "Disk: OK", "Network: Critical" };
string report = "System Health Report\n";
foreach (var status in statuses)
{
// I'm just appending each line to the existing string
report += status + "\n";
}
Console.WriteLine(report);
This looks clean, right? But here is the mistake: that report += status + "\n" line is a performance nightmare. Every single time that loop runs, C# allocates a new string in memory to hold the combined result. If I had 10,000 servers to report on, I'd be allocating 10,000 temporary strings. That's a lot of wasted memory and CPU cycles.
Switching to a mutable buffer
To fix this, we use StringBuilder. Think of it as a dynamic buffer—a piece of memory that can grow as you add to it without needing to recreate the entire object every time. I'll rewrite the report generator to use it.
using System.Text;
var statuses = new List<string> { "CPU: OK", "RAM: Warning", "Disk: OK", "Network: Critical" };
// I initialize the StringBuilder here
StringBuilder reportBuilder = new StringBuilder("System Health Report\n");
foreach (var status in statuses)
{
// Append adds to the existing buffer instead of creating a new string
reportBuilder.AppendLine(status);
}
// Only at the very end do I convert the buffer into a final string
string finalReport = reportBuilder.ToString();
Console.WriteLine(finalReport);
Notice I used AppendLine() instead of Append(). It's a handy shortcut that adds the current platform's newline character automatically, so I don't have to manually add \n.
Optimizing the initial capacity
If I really want to squeeze out more performance, there's one more trick. By default, StringBuilder starts with a small internal buffer. As you add more text, it has to "grow" that buffer (which involves allocating a larger array and copying the old data over). If I have a rough idea of how big my report will be, I can set the initial capacity upfront to avoid those resize operations.
I'll modify the initialization to tell the StringBuilder to start with 1024 characters of space:
// Starting with 1024 characters of capacity to prevent internal resizing
StringBuilder reportBuilder = new StringBuilder(1024);
reportBuilder.AppendLine("System Health Report");
It's a small detail, but in high-throughput systems, avoiding those internal re-allocations is what separates "working code" from "production-grade code."
📋 Practical Task
Building a CSV Data Exporter
You need to create a method that takes a list of Product objects and converts them into a single CSV-formatted string. Each product has a Name, a Price, and a Sku.
Requirements:
- Create a
Productclass with the properties mentioned. - Create a list of at least 5 products.
- Use a
StringBuilderto construct the CSV string. - The first line of the CSV must be the header:
Name,Price,Sku. - Each subsequent line should be the product data, comma-separated.
- The final result should be printed to the console using
ToString().
There are no comments for now.