Strings
Learn how to work with strings in C# — concatenation, interpolation, common methods, and StringBuilder.
On this page 10 sections
Strings Are Immutable
Once a string is created, it can never be changed for the rest of its lifetime. Every method that appears to modify a string — like ToUpper() or Replace() — actually returns a brand new string object, leaving the original completely untouched.
string original = "hello";string upper = original.ToUpper();Console.WriteLine(original); // "hello" — unchangedConsole.WriteLine(upper); // "HELLO" — a new stringCommon String Methods
| Method | Purpose |
|---|---|
| .Length | Number of characters |
| .ToUpper() / .ToLower() | Change case |
| .Trim() / .TrimStart() / .TrimEnd() | Remove leading/trailing whitespace |
| .Substring(start, length) | Extract part of a string |
| .Split(char) | Split into an array of strings |
| .Replace(old, new) | Replace all occurrences of a substring |
| .Contains(value) | Check if a substring exists |
| .StartsWith() / .EndsWith() | Check the beginning or end of a string |
| .IndexOf(value) | Find the position of a substring, or -1 if not found |
| .PadLeft(n) / .PadRight(n) | Pad a string to a fixed width |
string sentence = " Hello, C# World! ";Console.WriteLine(sentence.Trim().ToUpper());Console.WriteLine(sentence.Contains("C#"));Console.WriteLine(sentence.Trim().StartsWith("Hello"));Console.WriteLine("7".PadLeft(3, '0')); // "007"Click Run to see what this code prints.
Concatenation & Interpolation
As covered in the input/output lesson, string interpolation is the modern, preferred approach — it reads more naturally and handles formatting and alignment directly, without the visual clutter of many + operators.
string first = "Jane";string last = "Doe";
string full1 = first + " " + last; // concatenation — works, but less readable at scalestring full2 = $"{first} {last}"; // interpolation (preferred)
Console.WriteLine(full2);Comparing Strings
Unlike most reference types, == on strings compares their actual content, not just whether they're the same object in memory — a deliberate, useful exception to normal reference-type equality behavior.
string a = "hello";string b = "HELLO";
Console.WriteLine(a == b); // False — case-sensitive by defaultConsole.WriteLine(string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); // Truestring.Equals with an explicit StringComparison argument is generally considered best practice over == in production code, since it forces you to consciously decide whether the comparison should be case-sensitive, culture-aware, or ordinal — a decision == silently makes for you (case-sensitive, ordinal) without making that choice visible.
Splitting & Joining
Split() breaks a string into an array based on a delimiter; string.Join() does the reverse, combining an array (or any collection) back into one string with a separator.
string csv = "Alice,Bob,Charlie";string[] names = csv.Split(',');Console.WriteLine(names.Length); // 3Console.WriteLine(names[1]); // Bob
string rejoined = string.Join(" & ", names);Console.WriteLine(rejoined); // Alice & Bob & CharlieStringBuilder
Because strings are immutable, building one up in a loop with += creates a brand new string object on every single iteration — for a small number of iterations this is invisible, but for hundreds or thousands, it wastes significant memory and time. StringBuilder mutates an internal, resizable buffer instead, which is far more efficient for repeated appends.
using System.Text;
var sb = new StringBuilder();for (int i = 1; i <= 3; i++){ sb.Append($"Line {i}\n");}Console.WriteLine(sb.ToString());Reach for StringBuilder when building a string inside a loop with many iterations (dozens or more). For a handful of concatenations, regular strings and interpolation are perfectly fine and more readable — StringBuilder itself has a small amount of overhead that isn't worth it for trivial cases.
StringBuilder also supports several other useful methods beyond Append.
| Method | Purpose |
|---|---|
| .Append(value) | Adds text to the end |
| .AppendLine(value) | Adds text followed by a newline |
| .Insert(index, value) | Inserts text at a specific position |
| .Remove(start, length) | Removes a range of characters |
| .Replace(old, new) | Replaces text within the buffer |
| .ToString() | Converts the buffer to a final, regular string |
Common Beginner Mistakes
text.Trim(); on its own does nothing useful — the result must be captured: text = text.Trim();, since strings can never be modified in place.
This creates a new string object on every iteration — use StringBuilder for anything beyond a small, fixed number of concatenations.
== and string.Equals() default to case-sensitive comparison — explicitly pass StringComparison.OrdinalIgnoreCase when case shouldn't matter.
FAQs
Use string.Equals(a, b, StringComparison.OrdinalIgnoreCase) rather than converting both to the same case first with ToUpper()/ToLower(), which creates unnecessary intermediate string objects.
Conceptually yes — interpolation ($"...") is newer, more readable syntax that compiles down to essentially the same mechanism as string.Format("{0}", value); both support the same format specifiers.
It throws an ArgumentOutOfRangeException — always ensure start + length doesn't exceed the string's actual Length before calling Substring on untrusted input.
Yes — "" is a valid, zero-length string object; null means no string object exists at all. string.IsNullOrEmpty() and string.IsNullOrWhiteSpace() check for both cases at once, which is usually what you actually want.
Key Takeaways
- Strings are immutable — every "modifying" method returns a new string, never changing the original.
- Prefer string interpolation ($"...") over manual + concatenation for readability.
- Use string.Equals with an explicit StringComparison for comparisons where case-sensitivity matters.
- Use StringBuilder for building large strings inside loops with many iterations.
Summary
Strings are one of the most frequently used types in any real program, and understanding their immutability — plus when to reach for StringBuilder — will make your code both correct and efficient. Next, you'll learn how to organize reusable logic into methods.