Strings — Interpolation, Verbatim Literals and Common Methods

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.

🧠 Test Yourself

You concatenate 10,000 strings in a loop using result += item. Why is this slow and what is the correct fix?