Strings
Learn how to work with strings in C# — concatenation, interpolation, common methods, and StringBuilder.
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.