Why regular .Contains() will let you down (and how to fix it)
\n\nImagine: you are writing a book catalog search in C#. A user enters "Tolstoy", but the database stores "tolstoy" in lowercase. Regular string.Contains("Tolstoy") will return false. Sound familiar? This question has garnered thousands of views on Stack Overflow — and for good reason. In 2025, when .NET offers powerful tools for string manipulation, using the old approach means losing users and performance.
In this article, we will cover 5 best ways to make .Contains() case insensitive in C#. You will learn which method to choose for maximum speed, and which for correct handling of different languages. Let's go!
The problem: why is .Contains() case-sensitive by default?
\n\nIn C#, the string.Contains() method uses Ordinal comparison by default. This means characters are compared byte-by-byte: 'A' (0x41) is not equal to 'a' (0x61). For English-language content, this is often fine, but in multilingual applications (Russian, German, Turkish), this approach breaks search logic.
For example, in Turkish, the uppercase version of 'i' is 'İ', not 'I'. A simple ToLower() can give incorrect results. Therefore, simply calling text.ToLower().Contains(pattern.ToLower()) is bad practice. Let's look at modern solutions.
Method 1: StringComparison.OrdinalIgnoreCase — your new standard
\n\nStarting with .NET 5 (and working great in .NET 8/9), the fastest and safest way is to use the Contains overload with the StringComparison parameter:
string text = "War and Peace — Leo Tolstoy";\nstring searchTerm = "tolstoy";\n\nbool result = text.Contains(searchTerm, StringComparison.OrdinalIgnoreCase);\nConsole.WriteLine(result); // True\n\nWhy is this the best choice?
\n- \n
- Speed: OrdinalIgnoreCase works at the byte level but ignores case. It is 2-3 times faster than CurrentCultureIgnoreCase. \n
- Predictability: It does not depend on the user's culture. "ß" (German Eszett) will not equal "ss" unless you want it to. \n
- Support: Available in all modern versions of .NET (Core 2.1+). \n
Use this method when you need simple case-insensitive comparison and are not dealing with linguistic features (sorting, declensions).
\n\nMethod 2: IndexOf() with StringComparison — if you need more control
\n\nSometimes you need not just to check for existence, but also to find the position of the occurrence. Or you are working with older versions of .NET Framework where Contains with StringComparison is not available. In that case, IndexOf() comes to the rescue:
string text = "C# — the best language for backend";\nstring searchTerm = "best";\n\nbool result = text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0;\nConsole.WriteLine(result); // True\n\n// And if you need to get the position:\nint index = text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);\nConsole.WriteLine(index); // 4 (position where the word starts)\n\nWhen to use:
\n- \n
- In legacy projects (e.g., .NET Framework 4.x). \n
- When you need to find the index of the occurrence, not just check for its presence. \n
- For more complex scenarios (e.g., searching with character skipping). \n
Important: IndexOf returns -1 if the substring is not found. Always check for >= 0, not > 0.
Method 3: CultureInfo.CurrentCultureIgnoreCase — for linguistically correct search
\n\nIf your application must take into