Strings are one of the most-used types in any application — reading API request bodies, building JSON responses, formatting log messages, constructing file paths. C# has excellent string ergonomics: string interpolation avoids messy concatenation, verbatim literals handle file paths and multi-line SQL cleanly, and the string class has a comprehensive set of methods. Understanding string immutability and when to use StringBuilder prevents a common performance pitfall in loops and report generation.
String Literals
// ── Standard string — escape sequences active ────────────────────────────────
string path1 = "C:\\Users\\Alice\\Documents"; // \\ = literal backslash
string nl = "Line 1\nLine 2"; // \n = newline
// ── Verbatim string — @ prefix disables escaping ──────────────────────────────
string path2 = @"C:\Users\Alice\Documents"; // no escaping needed
string sql = @"SELECT id, title
FROM posts
WHERE status = 'published'
ORDER BY created_at DESC"; // multi-line works naturally
// ── String interpolation — $ prefix embeds expressions ────────────────────────
string firstName = "Alice";
int age = 30;
string greeting = $"Hello, {firstName}! You are {age} years old.";
string expr = $"Next year you will be {age + 1}.";
// Format specifiers inside interpolation braces
decimal price = 1999.99m;
string label = $"Price: {price:C}"; // currency: $1,999.99
string pct = $"Rate: {0.1567:P1}"; // percent 1dp: 15.7%
string date = $"Date: {DateTime.Now:yyyy-MM-dd}";
// ── Combined @$ — interpolated verbatim ───────────────────────────────────────
string user = "alice";
string dir = $@"C:\Users\{user}\Documents"; // C:\Users\alice\Documents
// ── Raw string literals (C# 11+) — triple-quoted, no escaping ─────────────────
string json = """
{
"name": "Alice",
"role": "admin"
}
""";
Note: String interpolation compiles to a highly optimised string creation — in .NET 6+ the compiler uses
DefaultInterpolatedStringHandler which avoids boxing and intermediate allocations for most cases. It is both the most readable and the most performant general approach for building strings. Use string.Format() only when the format string itself is loaded dynamically from a resource file or configuration value you cannot hardcode at compile time.Tip: Never build SQL queries by concatenating strings with user input — this creates SQL injection vulnerabilities. Even in this strings chapter, it is worth establishing the rule: always use parameterised queries or Entity Framework (covered in Part 4 and Part 6). The pattern
$"SELECT * FROM users WHERE email = '{userEmail}'" allows an attacker to inject SQL. The correct pattern uses named parameters like @email that are passed separately from the query string.Warning: String comparisons in C# are case-sensitive and culture-sensitive by default.
"hello" == "Hello" is false. For case-insensitive comparisons — email addresses, usernames, slugs — always use string.Equals(a, b, StringComparison.OrdinalIgnoreCase). The Ordinal variant ignores locale-specific sorting rules, which is almost always correct for identifiers and API parameters in ASP.NET Core applications.Common String Methods
string s = " Hello, World! ";
// Whitespace
s.Trim() // "Hello, World!"
s.TrimStart() // "Hello, World! "
s.TrimEnd() // " Hello, World!"
// Case
"hello".ToUpper() // "HELLO"
"HELLO".ToLower() // "hello"
// Search and test
"Hello".Contains("ell") // true
"Hello".StartsWith("He") // true
"Hello".EndsWith("lo") // true
"Hello".IndexOf('l') // 2 (-1 if not found)
// Transform
"Hello World".Replace("World", "C#") // "Hello C#"
"a,b,,c".Split(',') // ["a","b","","c"]
"a,b,,c".Split(',',
StringSplitOptions.RemoveEmptyEntries) // ["a","b","c"]
string.Join(" | ", new[]{"a","b","c"}) // "a | b | c"
"Hello World".Substring(6) // "World"
"Hello World"[6..] // "World" — C# 8 range syntax
"Hello World"[..5] // "Hello"
// Null / empty checks
string.IsNullOrEmpty(s) // true if null or ""
string.IsNullOrWhiteSpace(s) // true if null, "", or " "
// Case-insensitive comparison
string.Equals("ALICE", "alice",
StringComparison.OrdinalIgnoreCase) // true
StringBuilder for Loop Concatenation
// WRONG — O(n²): creates a new string object on every += iteration
string result = "";
for (int i = 0; i < 10_000; i++)
result += $"Item {i}\n"; // copies all previous characters each time!
// CORRECT — O(n): StringBuilder maintains a mutable buffer
var sb = new System.Text.StringBuilder(capacity: 80_000);
for (int i = 0; i < 10_000; i++)
sb.AppendLine($"Item {i}");
string result2 = sb.ToString(); // single allocation at the very end
Common Mistakes
Mistake 1 — Case-sensitive equality on identifiers
❌ Wrong — fails when user types “ALICE@EXAMPLE.COM”:
if (inputEmail == storedEmail) // "ALICE@EXAMPLE.COM" != "alice@example.com"
✅ Correct:
if (string.Equals(inputEmail, storedEmail, StringComparison.OrdinalIgnoreCase))
Mistake 2 — String concatenation in a loop
❌ Wrong — quadratic time and memory for large inputs.
✅ Correct — use StringBuilder for any loop that incrementally builds a string.