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
143: Culture-Aware String Comparison
I've seen more bugs creep into production systems because of "simple" string comparisons than I care to admit. On the surface, checking if two strings are equal seems like a solved problem—you use == or .Equals() and move on. But strings aren't just sequences of bytes; they represent human language, and human language is messy. If you treat every string as a raw sequence of characters, you're going to run into trouble the moment your app hits a user in a different locale.
The danger of the "simple" equality check
The most common mistake I see is the "Normalize to Lowercase" pattern. You know the one: if (input.ToLower() == "admin"). It feels safe because you're forcing everything into a common case. But this is a performance hit—you're allocating a brand new string just to throw it away a millisecond later—and more importantly, it's linguistically naive.
In C#, the default == operator does an ordinal comparison. It looks at the underlying Unicode values. If the values are identical, it's a match. This is perfect for internal IDs or machine-readable keys, but it's a disaster for user-facing data. For example, if you're comparing strings based on the user's current culture, some languages treat different characters as equivalent, while others have entirely different rules for casing.
When the alphabet betrays you
Let me give you a specific example that has haunted developers for decades: the Turkish "I". In English, the uppercase of 'i' is 'I'. Simple, right? But in Turkish, there are two versions of the letter I: the dotted İ and the dotless ı.
// This might behave differently depending on the machine's culture settings!
string input = "file";
if (input.ToUpper() == "FILE")
{
// In a Turkish culture, "file".ToUpper() becomes "FİLE"
// So "FİLE" == "FILE" is FALSE.
}
If your server is running in a Turkish locale, your case-insensitive check for "FILE" just failed. I can't tell you how many "impossible" bugs have been traced back to a server migration where the OS culture settings changed, suddenly breaking logic that worked perfectly on a developer's laptop in Seattle.
Choosing your comparison strategy
The professional way to handle this is to be explicit. Stop using == or .ToLower() for comparisons and start using the StringComparison enum. It forces you to decide: "Am I comparing data for a machine, or am I comparing text for a human?"
- StringComparison.Ordinal: This is the fastest. It compares the raw binary values. Use this for file paths, dictionary keys, XML tags, or any internal identifier. It is culture-blind and consistent across every machine on earth.
- StringComparison.OrdinalIgnoreCase: Same as ordinal, but ignores case. This is usually what you actually want when you're checking something like a "Role" or "Status" string.
- StringComparison.CurrentCulture: This uses the rules of the user's current OS settings. Use this when you're sorting a list of names to be displayed in a UI. It ensures that the sorting feels "correct" to the person looking at the screen.
- StringComparison.InvariantCulture: This is a middle ground. It's based on a stable, consistent culture (essentially English) that doesn't change based on the user's machine. It's great for data that needs to be persisted to a file and read back later by a different user in a different country.
My rule of thumb is simple: if the string is a "token" (something the computer cares about), go Ordinal. If the string is "text" (something a human reads), go Culture. Being explicit doesn't just prevent bugs; it tells the next developer exactly why you chose that specific comparison.
📋 Practical Task
The Global Username and Promo Code Validator
You are building a registration system for a global app. You need to implement a validation method that handles two different types of strings with different rules:
- Promo Codes: These are machine-generated codes (e.g., "SUMMER2024"). They must be compared using a case-insensitive, culture-blind approach to ensure they work the same way for every user regardless of their region.
- User Display Names: These are human-readable names. You need to check if a new display name is identical to an existing one using the current culture's rules.
Write a class ValidationService with two methods: bool IsPromoCodeValid(string input, string actualCode) and bool IsDisplayNameDuplicate(string input, string existingName). Use the appropriate StringComparison enum values to ensure the promo code is ordinal and the display name is culture-aware.
There are no comments for now.