Welcome back! On Day 7 you learned to stop repeating yourself with methods. Today we're diving deep into one of the most-used types in all of programming: strings. You've been using them since Day 1 โ but trust me, you've barely scratched the surface. ๐ค
1. What Even Is a String, Really?
You already know a string is text in double quotes: "Hello". But under the hood, a string in C# is an immutable sequence of characters. Think of it like a frozen necklace of beads โ each bead is a char, and once the necklace is made, you can't swap individual beads. You can only make a new necklace.
string greeting = "Hello";
// greeting[0] = 'J'; // โ Compiler error! Strings are immutable.
Immutable means once a string is created, it cannot be changed. Every "modification" actually creates a brand-new string. This matters for performance later โ but for now, just know: strings are read-only.
2. The String Toolkit โ Essential Methods
C# gives you a massive toolbox for working with strings. Here are the greatest hits:
string name = " Farhad Mammadov ";
Console.WriteLine(name.Length); // 19 (includes spaces)
Console.WriteLine(name.Trim()); // "Farhad Mammadov"
Console.WriteLine(name.Trim().ToUpper()); // "FARHAD MAMMADOV"
Console.WriteLine(name.Trim().ToLower()); // "farhad mammadov"
These methods return new strings โ they don't modify the original (remember: immutable!).
Here's a quick reference of the methods you'll use every day:
Trim()โ removes whitespace from both endsTrimStart()/TrimEnd()โ removes whitespace from one endToUpper()/ToLower()โ changes caseContains("text")โ returnstrueif the substring existsStartsWith("text")/EndsWith("text")โ checks prefix/suffixIndexOf("text")โ returns position of first match, or-1if not foundReplace("old", "new")โ swaps all occurrencesInsert(index, "text")โ inserts text at a positionRemove(startIndex, count)โ removes charactersPadLeft(totalWidth)/PadRight(totalWidth)โ pads with spaces (great for alignment)
3. Searching Inside Strings
Need to find something? You've got options:
string sentence = "The quick brown fox jumps over the lazy dog";
Console.WriteLine(sentence.Contains("fox")); // True
Console.WriteLine(sentence.StartsWith("The")); // True
Console.WriteLine(sentence.EndsWith("cat")); // False
Console.WriteLine(sentence.IndexOf("brown")); // 10
Console.WriteLine(sentence.LastIndexOf("the")); // 31
Pro tip: Contains, StartsWith, and EndsWith all accept a StringComparison parameter for case-insensitive searches:
bool found = sentence.Contains("FOX", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(found); // True
Always use StringComparison.OrdinalIgnoreCase instead of converting to upper/lower first โ it's faster and handles edge cases in other languages better.
4. Slicing with Substring and Ranges
Need a piece of a string? Two approaches:
string full = "Hello, World!";
// Classic: Substring(startIndex, length)
string world = full.Substring(7, 5);
Console.WriteLine(world); // "World"
// Modern C# way: Range operator (C# 8+)
string worldRange = full[7..12];
Console.WriteLine(worldRange); // "World"
// From index to end
string fromComma = full[5..];
Console.WriteLine(fromComma); // ", World!"
// Last N characters using ^
string lastSix = full[^6..];
Console.WriteLine(lastSix); // "World!"
The range operator (..) is the modern, cleaner way. ^ means "from the end." full[^6..] reads as "start 6 from the end, go to the end."
5. Splitting and Joining
Splitting a string into pieces and putting them back together โ bread and butter of text processing:
string csv = "apple,banana,cherry,date";
string[] fruits = csv.Split(',');
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
// apple
// banana
// cherry
// date
// Join them back with a different separator
string result = string.Join(" | ", fruits);
Console.WriteLine(result); // "apple | banana | cherry | date"
Split also handles multiple separators and can remove empty entries:
string messy = "one,,two,,,three";
string[] clean = messy.Split(',', StringSplitOptions.RemoveEmptyEntries);
// clean = ["one", "two", "three"]
6. String Interpolation โ The Right Way to Build Strings
You already know $"Hello, {name}!" from Day 2. But interpolation can do more:
decimal price = 19.99m;
int quantity = 3;
// Format specifiers inside interpolation
Console.WriteLine($"Total: {price * quantity:C}"); // Total: $59.97 (currency)
Console.WriteLine($"Tax: {0.08675309:P2}"); // Tax: 8.68% (percent, 2 decimals)
Console.WriteLine($"Hex: {255:X}"); // Hex: FF
Console.WriteLine($"Padded: {42:D5}"); // Padded: 00042
Console.WriteLine($"Date: {DateTime.Now:yyyy-MM-dd}"); // Date: 2026-04-06
The format specifier goes after the : inside the braces. The most common ones:
Cโ currencyN2โ number with 2 decimal places and thousand separatorsPโ percentageD5โ integer padded to 5 digitsXโ hexadecimalyyyy-MM-ddโ date formatting
7. StringBuilder โ When Performance Matters
Remember how strings are immutable? That means this innocent-looking loop creates 1,000 temporary string objects:
// โ Slow โ creates a new string every iteration
string result = "";
for (int i = 0; i < 1000; i++)
{
result += $"Line {i}\n";
}
Enter StringBuilder โ a mutable buffer that builds strings efficiently:
using System.Text;
// โ
Fast โ modifies the same buffer
StringBuilder sb = new();
for (int i = 0; i < 1000; i++)
{
sb.AppendLine($"Line {i}");
}
string result = sb.ToString();
Rule of thumb: if you're concatenating in a loop or building a string from many pieces, use StringBuilder. For 2-3 concatenations? Regular interpolation is fine.
Key StringBuilder methods:
Append("text")โ adds to the endAppendLine("text")โ adds text + newlineInsert(index, "text")โ inserts at positionReplace("old", "new")โ replaces in-placeClear()โ empties the bufferToString()โ produces the finalstring
8. Verbatim and Raw String Literals
Tired of escaping backslashes in file paths? C# has you covered:
// Regular string โ must escape backslashes
string path1 = "C:\\Users\\farhad\\Documents";
// Verbatim string (@) โ backslashes are literal
string path2 = @"C:\Users\farhad\Documents";
// Raw string literal (C# 11+) โ triple quotes, no escaping needed
string json = """
{
"name": "Farhad",
"blog": "farhad.su"
}
""";
Raw string literals ("""...""") are the newest addition. They preserve indentation relative to the closing """ and need zero escaping. Perfect for JSON, SQL, HTML, or any multi-line text.
You can even use interpolation in raw strings with multiple $ signs:
string name = "Farhad";
string raw = $$"""
{
"name": "{{name}}",
"year": 2026
}
""";
The number of $ signs tells C# how many braces trigger interpolation. $$ means {{ }} is the interpolation syntax, so literal { } in JSON doesn't cause problems.
9. Comparing Strings โ It's More Nuanced Than You Think
string a = "hello";
string b = "Hello";
// Case-sensitive (default)
Console.WriteLine(a == b); // False
Console.WriteLine(string.Equals(a, b)); // False
// Case-insensitive
Console.WriteLine(string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); // True
// For sorting/display โ use culture-aware comparison
Console.WriteLine(string.Compare(a, b, StringComparison.CurrentCulture));
Best practices:
- Use
==orstring.Equalsfor exact matches - Use
StringComparison.OrdinalIgnoreCasefor case-insensitive logic (usernames, emails, etc.) - Use
StringComparison.CurrentCultureonly when you need locale-aware sorting - Never use
ToLower() == ToLower()for comparison โ it's slower and breaks in Turkish (the infamous Turkish-ฤฐ problem)
10. Your Homework: Word Counter
Build a program that takes a sentence and reports:
- Total character count (with and without spaces)
- Word count
- The longest word
- The sentence reversed (word by word, not character by character)
string input = "The quick brown fox jumps over the lazy dog";
// Character counts
Console.WriteLine($"Characters (with spaces): {input.Length}");
Console.WriteLine($"Characters (no spaces): {input.Replace(" ", "").Length}");
// Word count
string[] words = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine($"Words: {words.Length}");
// Longest word
string longest = "";
foreach (string word in words)
{
if (word.Length > longest.Length)
longest = word;
}
Console.WriteLine($"Longest word: {longest}");
// Reverse word order
Array.Reverse(words);
Console.WriteLine($"Reversed: {string.Join(" ", words)}");
Bonus challenge: make the word count case-insensitive and count unique words only. (Hint: you'll want a HashSet<string> โ we haven't covered it yet, but new HashSet<string>(words, StringComparer.OrdinalIgnoreCase).Count does the trick.)
For the complete string reference, see the System.String docs on Microsoft Learn.
Summary of Day 8
- Strings are immutable โ every modification creates a new string.
- The string toolkit (
Trim,Contains,Replace,Split,Join) covers 90% of everyday text work. - Range operators (
[7..12],[^6..]) are the modern way to slice strings. - String interpolation with format specifiers (
{value:C},{value:N2}) handles formatting elegantly. StringBuilderis your friend for loops and heavy concatenation โ avoids creating thousands of throwaway strings.- Raw string literals (
"""...""") eliminate escaping headaches for JSON, paths, and multi-line text. - Always use
StringComparison.OrdinalIgnoreCaseinstead ofToLower()for case-insensitive comparisons.
Tomorrow: we'll talk about Error Handling with try/catch โ because your code will crash, and the difference between a junior and a senior dev is how gracefully they handle it. Time to stop letting exceptions ruin your day. ๐ฅ
See you on Day 9!