C#
String.Contains
case insensitive
duplicate
programming tips

How to make String.Contains case insensitive?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A case-insensitive substring check sounds simple, but the details matter in C#. The best solution depends on your target framework and on whether you want ordinal comparison semantics or culture-aware text behavior.

The modern solution in C#

In current .NET versions, string.Contains has an overload that accepts StringComparison. This is usually the cleanest and most efficient answer.

csharp
1using System;
2
3string text = "Hello World";
4bool found = text.Contains("hello", StringComparison.OrdinalIgnoreCase);
5
6Console.WriteLine(found);

StringComparison.OrdinalIgnoreCase is often the right default for identifiers, protocol values, file extensions, and most programmatic matching. It performs a case-insensitive comparison based on Unicode code points rather than user-locale sorting rules.

When to use culture-aware comparisons

If the comparison is part of user-facing text behavior, you may want CurrentCultureIgnoreCase or InvariantCultureIgnoreCase instead.

csharp
1using System;
2
3string text = "Istanbul";
4bool found = text.Contains("ISTANBUL", StringComparison.InvariantCultureIgnoreCase);
5
6Console.WriteLine(found);

The important point is intent. Ordinal comparisons are best for machine-style matching. Culture-aware comparisons are for human language scenarios such as UI search or localized text processing.

Older framework fallback

If your target framework does not support the Contains overload, use IndexOf with StringComparison and check whether the result is non-negative.

csharp
1using System;
2
3string text = "Hello World";
4bool found = text.IndexOf("hello", StringComparison.OrdinalIgnoreCase) >= 0;
5
6Console.WriteLine(found);

This is the traditional fallback and still a perfectly valid approach.

Why ToLower() and ToUpper() are weaker solutions

A common workaround is to normalize both strings with ToLower() or ToUpper() and then call Contains. That works in simple cases, but it is usually a worse choice.

csharp
1using System;
2
3string text = "Hello World";
4bool found = text.ToUpperInvariant().Contains("HELLO");
5
6Console.WriteLine(found);

The issues are:

  • it allocates new strings
  • it can hide culture-related bugs
  • it is less explicit about comparison intent

Passing StringComparison directly avoids those extra allocations and expresses the rule more clearly.

Null handling and reusable helpers

If the source string or search value may be null, decide that behavior explicitly instead of letting random call sites handle it differently.

csharp
1using System;
2
3public static class StringExtensions
4{
5    public static bool ContainsIgnoreCase(this string source, string value)
6    {
7        if (source is null) throw new ArgumentNullException(nameof(source));
8        if (value is null) throw new ArgumentNullException(nameof(value));
9
10        return source.Contains(value, StringComparison.OrdinalIgnoreCase);
11    }
12}
13
14Console.WriteLine("Codemia".ContainsIgnoreCase("CODE"));

A helper like this prevents ad hoc mixes of ordinal, invariant, and culture-sensitive rules across the codebase.

Test the comparison mode you choose

Because string comparison rules affect behavior in subtle ways, it is worth writing a small unit test around whichever mode you standardize on.

csharp
1using System;
2using Xunit;
3
4public class StringTests
5{
6    [Fact]
7    public void ContainsIgnoreCase_UsesOrdinalRules()
8    {
9        Assert.True("Release-Notes".Contains("release", StringComparison.OrdinalIgnoreCase));
10    }
11}

A tiny test like this makes framework upgrades or helper refactors much safer.

Common Pitfalls

The most common mistake is using ToLower() or ToUpper() everywhere without deciding which comparison semantics are actually required. This can produce subtle bugs in internationalized applications.

Another problem is choosing a culture-aware comparison for machine identifiers. For keys, tokens, MIME types, and file names, OrdinalIgnoreCase is usually the safer choice.

It is also easy to forget framework compatibility. If your project targets an older runtime, the Contains overload may not exist, and IndexOf becomes the compatible option.

Summary

  • In modern C#, use string.Contains(value, StringComparison.OrdinalIgnoreCase) for most case-insensitive substring checks.
  • Use culture-aware comparisons only when text behavior should follow language rules.
  • On older frameworks, use IndexOf(..., StringComparison...) >= 0.
  • Avoid normalizing with ToLower() or ToUpper() unless you have a specific reason.
  • Pick one comparison strategy deliberately instead of mixing several by accident.

Course illustration
Course illustration