Skip to main content
CodeOath
← All posts

C#64 min total · 17 parts

C# Fundamentals: Value Types, Reference Types, Boxing, and the Type System

Part 4 of 17 · ~2 min

Strings: Immutable Reference Types

string sits in an odd spot: it's a reference type, like Shipment, but with one rule that makes it behave nothing like one. A string can never be changed in place. Every operation that looks like it edits a string is actually building a brand-new one and handing that back.

string sku = "WDG-100";
string label = sku;
label += "-DISCONTINUED";

Console.WriteLine(sku); // "WDG-100" — completely unaffected

label's += never reaches back into whatever sku is pointing at — under the hood it builds a fresh string holding the combined characters, then quietly swaps label over to point at that new one. sku was never in the blast radius, because there's no operation in the language that reaches into an existing string and edits it. It also explains a common dead-code mistake: write sku.Trim(); as its own statement and walk away, and you've accomplished nothing at all — Trim() produced a value, that value went nowhere, and it's gone.

There's a real upside to all this: nothing can ever be caught mid-edit, so a string can be a dictionary key or passed freely between threads without a single lock anywhere in sight. The downside shows up under a different kind of pressure — assemble a long string one small piece at a time using +=, and each individual append has to allocate a completely new string and re-copy everything built up so far, just to tack a few more characters onto the end.

// O(n²) in the length of the finished manifest — every += recopies everything built up to that point
string manifest = "";
foreach (var sku in shipment.PickedSkus)
{
    manifest += sku + "\n";
}

// StringBuilder holds one growable buffer and appends into it directly — amortized O(1) per call
var sb = new StringBuilder();
foreach (var sku in shipment.PickedSkus)
{
    sb.AppendLine(sku);
}
string manifest2 = sb.ToString();

A five-line pick ticket, nobody notices. A shipment manifest for a pallet with four hundred SKUs on it, and the += version is doing real, measurable, unnecessary work — recopying an ever-larger string on every single line for no reason other than that a growable buffer wasn't used.