Learning outcome
By the end of this lesson, you should be able to explain three closely related ideas: a C# string is immutable, assignment copies the reference rather than the text, and most operations that look like edits actually allocate a new string. This matters in interviews because it explains surprising behavior, performance tradeoffs, and why strings are safe to share across APIs and threads.
Intuition
Think of a string as a sealed label attached to a piece of text. You can hand the label to multiple variables, but you cannot edit the text inside the sealed container. If you need different text, the runtime creates a different container and points a variable at it.
That is why this kind of code can feel like mutation while still preserving the old value:
After the concatenation, alias still refers to the old text, because nothing happened to that old instance. A new string was created and assigned to original.
Deep dive
C# string is a reference type whose contents are read-only after construction. The runtime stores the characters in an internal sequence, and methods such as Replace, Substring, ToUpperInvariant, and concatenation operators return a new string instead of editing the existing object.
This has a few practical consequences:
- Assignment shares references, not text copies.
Both variables initially point to the same string object.
- Apparent edits produce new objects.
b still sees cat because Replace created a new string.
- Literals are already strings.
Every literal like "hello" creates a System.String instance in the program’s string handling model. Reusing the same literal text may allow sharing, but you should reason about behavior, not identity.
- Immutability improves safety.
Because no code can change a string instance in place, you can pass strings between methods, cache them, and use them across threads without worrying that another caller will alter the same object underneath you.
- Performance changes with repeated edits.
Repeated concatenation in a loop can allocate many intermediate strings. For many sequential edits, prefer StringBuilder so the intermediate work happens in a mutable buffer and the final ToString() creates one result.
The key interview phrase is: strings are immutable, but variables are not. The variable can point somewhere else; the string object itself does not change.
Failure modes
Common mistakes to watch for:
- Assuming
Replace,Trim,ToUpperInvariant, or+=changes the original string in place. - Expecting an alias variable to reflect later “modifications” made through another reference.
- Using string concatenation in large loops and then being surprised by allocation overhead.
- Confusing immutability with interned string identity. Two equal strings can compare as equal without being the same object reference.
A useful mental model is: if the API sounds like it edits text, ask yourself whether it returns a new string instead.
Interview drill
Try answering these out loud:
- Why does
s2keep the old value afters1 += "x"? - Why is
stringsafe to share across threads? - When would you choose
StringBuilderover repeated concatenation? - What is the difference between changing a variable reference and changing the object it refers to?
- Why can immutable objects still be highly useful in performance-critical code?
Revision checklist
stringis immutable.- Assigning one string variable to another copies the reference.
+=,Replace,Substring,Trim, and case conversion methods return new strings.- Old string instances remain unchanged unless no references remain.
- Use
StringBuilderfor many sequential edits. - Immutability helps correctness, sharing, and thread safety.
Production code
The program below shows the two most important behaviors: the original string remains unchanged after a “modification,” and an alias keeps the old value. It also demonstrates why building text incrementally with StringBuilder is the better tool for repeated edits.
Code walkthrough
originalandaliasstart out pointing to the same string object.modified = original + "world"does not alter the old object; it creates a new one.object.ReferenceEquals(original, alias)isTrueimmediately after assignment because both variables refer to the same object.object.ReferenceEquals(original, modified)isFalsebecause concatenation produced a different string instance.- The
StringBuilderloop shows the standard pattern for many edits: append into a mutable buffer, then convert once at the end.
A good interview answer should mention both semantics and performance: immutability gives predictable behavior, while StringBuilder avoids the cost of producing many temporary strings.
Executable code examples
Immutability and aliasing demo
Program.cs
using System;
using System.Text;
var original = "Hello ";
var alias = original;
var modified = original + "world";
Console.WriteLine(original);
Console.WriteLine(alias);
Console.WriteLine(modified);
Console.WriteLine(object.ReferenceEquals(original, alias));
Console.WriteLine(object.ReferenceEquals(original, modified));
var builder = new StringBuilder();
for (int i = 1; i <= 3; i++)
{
builder.Append("item ").Append(i).Append(';');
}
Console.WriteLine(builder.ToString());