C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
116: Regular Expressions in .NET
I've spent a lot of time reviewing PRs where a developer tried to "save time" by avoiding regular expressions. They usually do this because regex looks like a cat walked across the keyboard, and that's a fair first impression. But the alternative—manual string manipulation—is where the real danger lies. I want to show you why relying on basic string methods for pattern matching is a trap, and how to use the .NET Regex engine without killing your application's performance.
The fragility of manual string slicing
Imagine you're processing a stream of logs, and you need to extract a Transaction ID that always looks something like TXN-12345-AB. When you first see the data, you might be tempted to do something like this:
string logEntry = "2023-10-01 12:00:00 [INFO] Processed TXN-98765-XY successfully";
int start = logEntry.IndexOf("TXN-") + 4;
int end = logEntry.IndexOf(" ", start);
string txnId = logEntry.Substring(start, end - start);
This looks clean, right? It's fast, it's readable, and it works—for exactly one specific scenario. But this is what I call "happy path coding." The moment your log format changes slightly—maybe a developer adds an extra space, or the ID moves to the end of the line where there is no trailing space—your code throws an ArgumentOutOfRangeException or returns a chunk of the string you didn't want. You end up writing five different if statements and three more IndexOf calls just to handle the edge cases. It becomes a brittle mess that's actually harder to read than a regex pattern.
Letting the Regex engine do the heavy lifting
This is where System.Text.RegularExpressions comes in. Instead of telling the computer how to slice the string, you describe what the string should look like. For our Transaction ID, we want a literal "TXN-", followed by five digits, another dash, and two uppercase letters.
using System.Text.RegularExpressions;
string logEntry = "2023-10-01 12:00:00 [INFO] Processed TXN-98765-XY successfully";
string pattern = @"TXN-\d{5}-[A-Z]{2}";
Match match = Regex.Match(logEntry, pattern);
if (match.Success)
{
string txnId = match.Value;
// Now we have our ID, regardless of where it sits in the string
}
Notice the @ before the string. That's a verbatim string literal; it prevents C# from treating backslashes (like \d) as escape characters for the language itself, passing them directly to the regex engine. Now, if the log format changes or the ID moves, the code doesn't break. It either finds the pattern or it doesn't. That's a massive jump in reliability.
Solving the performance hit with Source Generators
Now, here is the "senior engineer" caveat. If you use Regex.Match(input, pattern) inside a loop that runs a million times, you're going to hit a performance wall. By default, .NET has to parse that pattern string and compile it into an internal representation every time it's called (or fetch it from a small internal cache). In the old days, we used RegexOptions.Compiled to bake the regex into MSIL, but that increased startup time and memory usage.
If you're using .NET 7 or newer, we have a much better way: Source Generators. Instead of parsing the regex at runtime, the compiler generates the C# code to handle the matching during the build process. It's incredibly fast. You do it by defining a partial method with the [GeneratedRegex] attribute:
public partial class LogParser
{
[GeneratedRegex(@"TXN-\d{5}-[A-Z]{2}")]
private static partial Regex TransactionIdRegex();
public string ExtractId(string input)
{
var match = TransactionIdRegex().Match(input);
return match.Success ? match.Value : string.Empty;
}
}
By doing this, you get the best of both worlds: the expressive power of regular expressions and the raw speed of hand-written parsing logic. I always push my team toward this approach because it moves the "cost" of the regex from the user's CPU to the developer's build time.
📋 Practical Task
Build a Software License Key Validator
You need to implement a validation check for a software license key. The key must follow these strict rules:
- It must start with the prefix "LIC-"
- It must be followed by exactly 4 alphanumeric characters (uppercase letters or digits)
- It must have a hyphen
- It must end with exactly 4 alphanumeric characters (uppercase letters or digits)
- Example of a valid key:
LIC-A1B2-C3D4
- Create a partial class named
LicenseManager. - Implement a source-generated regex using the
[GeneratedRegex]attribute that matches this specific pattern. - Write a method
bool IsValidLicense(string key)that returnstrueonly if the entire string matches the pattern perfectly (hint: use^and$in your regex to ensure it doesn't just match a part of the string). - Test it with
LIC-1234-ABCD(should be true) andLIC-123-ABCD(should be false).
There are no comments for now.