How to Check If a String Contains a Character in C#

Need to know if a name, email or code has a certain letter in it? In C#, text.Contains('M') answers that in one call and returns true or false.

In this tutorial, I’ll walk one customer name, Jenna Morales, through each C# string contains character check worth knowing on .NET 10:

string customer = "Jenna Morales";

Console.WriteLine(customer.Contains('M'));
Console.WriteLine(customer.Contains('z'));

Output:

True
False

'M' is in “Jenna Morales”, so the first line prints True. There’s no 'z', so the second prints False.

String.Contains(char) Syntax and Overloads in C#

The single quotes matter: 'M' is a char, while "M" is a one-character string. Both work, but each one picks a different overload (see C# data types for how char differs from string).

MethodReturnsAvailable in
Contains(char)bool.NET Core 2.1+, .NET 5-10
Contains(char, StringComparison)bool.NET Core 2.1+, .NET 5-10
Contains(string)boolAll versions, incl. .NET Framework
IndexOf(char)int (-1 if missing)All versions, incl. .NET Framework

Microsoft’s analyzer rule CA1847 flags Contains("M") and suggests Contains('M'), because the char overload is faster.

On .NET Framework 4.8 there’s no char overload, so use IndexOf('M') >= 0 there. Not sure which runtime you’re on? Here’s how to check your .NET version.

How Do You Check If a String Contains a Character Ignoring Case?

Contains(char) is ordinal, which means case-sensitive. Same name, now looking for a lowercase 'm':

string customer = "Jenna Morales";

Console.WriteLine(customer.Contains('m'));
Console.WriteLine(customer.Contains('m', StringComparison.OrdinalIgnoreCase));
Console.WriteLine(customer.Contains("M"));

// Older way: lower-case the char and the string first
char wanted = 'J';
Console.WriteLine(customer.ToLowerInvariant().Contains(char.ToLowerInvariant(wanted)));

Output:

False
True
True
True
VS Code running ContainsIgnoreCase.cs: C# string Contains('m') returns False, Contains('m', StringComparison.OrdinalIgnoreCase) returns True
Contains(‘m’) misses the capital M until you pass OrdinalIgnoreCase

The plain call prints False because the name only has a capital M. Passing StringComparison.OrdinalIgnoreCase flips it to True.

The last line is the old trick: lower-case both sides with ToLowerInvariant. It works on any version, but it allocates a new string, so I only use it on .NET Framework.

Find the Character’s Position with IndexOf and LastIndexOf

When you need to know where the character is, not just whether it’s there, switch to IndexOf:

string customer = "Jenna Morales";

int first = customer.IndexOf('a');
int last = customer.LastIndexOf('a');
int missing = customer.IndexOf('z');

Console.WriteLine($"First 'a' at {first}, last 'a' at {last}");
Console.WriteLine($"'z' at {missing}, found: {missing >= 0}");
Console.WriteLine($"Space at {customer.IndexOf(' ')}");
Console.WriteLine($"First name: {customer[..customer.IndexOf(' ')]}");

Output:

First 'a' at 4, last 'a' at 9
'z' at -1, found: False
Space at 5
First name: Jenna
VS Code running IndexOfChar.cs: C# IndexOf('a') returns 4, LastIndexOf('a') returns 9 and IndexOf('z') returns -1 for Jenna Morales
IndexOf finds the first match, LastIndexOf the last, and -1 means not found

The first 'a' sits at index 4 and the last at 9, since indexes start at 0. 'z' returns -1, so IndexOf(c) >= 0 is the classic contains check.

The space at index 5 lets the range customer[..5] cut out “Jenna”. It’s the same idea I use to get the first N characters of a string.

Check for Any of Several Characters with IndexOfAny, ContainsAny and SearchValues

Validation usually asks a different question: does this username contain any forbidden character? Here are four usernames checked against @, #, $ and a space:

using System.Buffers;

string[] usernames = ["jenna.morales", "tyler#brooks", "maria_lopez", "evan@reed"];
char[] bad = ['@', '#', '$', ' '];
SearchValues<char> badSet = SearchValues.Create("@#$ ");

foreach (string user in usernames)
{
    int at = user.IndexOfAny(bad);
    bool spanHit = user.AsSpan().ContainsAny(badSet);
    Console.WriteLine($"{user,-14} IndexOfAny={at,2}  ContainsAny={spanHit}");
}

string evan = "evan@reed";
Console.WriteLine(evan.Contains('@'));
Console.WriteLine(evan.IndexOf('@'));
Console.WriteLine(evan.AsSpan().ContainsAny('@', '#'));

Output:

jenna.morales  IndexOfAny=-1  ContainsAny=False
tyler#brooks   IndexOfAny= 5  ContainsAny=True
maria_lopez    IndexOfAny=-1  ContainsAny=False
evan@reed      IndexOfAny= 4  ContainsAny=True
True
4
True
VS Code running ContainsAnyChar.cs: C# IndexOfAny and AsSpan().ContainsAny with SearchValues flag tyler#brooks and evan@reed as invalid usernames
IndexOfAny gives the position, ContainsAny with SearchValues gives a fast true/false

tyler#brooks fails at index 5 and evan@reed at index 4. The other two pass with -1 and False.

IndexOfAny(char[]) works on every .NET version. AsSpan().ContainsAny needs .NET 8+, and SearchValues.Create builds an optimized set you should create once and reuse, ideally in a static readonly field.

For two or three characters, ContainsAny('@', '#') skips the set entirely. If you’re matching whole words instead of characters, see how to compare a string with multiple values.

Note: Regex.IsMatch(user, "[@#$ ]") gives the same answer, but a regex is overkill for a fixed character list.

Count Character Occurrences with LINQ Any and Count

LINQ helps when the question is a category, like “any digit?”, or a count. Here’s a US city line with a ZIP code:

string zipLine = "Austin, TX 78701";

Console.WriteLine(zipLine.Any(char.IsDigit));
Console.WriteLine(zipLine.Any(char.IsLower));
Console.WriteLine(zipLine.All(c => c != '!'));

int digits = zipLine.Count(char.IsDigit);
int ts = zipLine.Count(c => c == 'T' || c == 't');
Console.WriteLine($"Digits: {digits}, T or t: {ts}");
Console.WriteLine($"Spaces: {zipLine.AsSpan().Count(' ')}");

Output:

True
True
True
Digits: 5, T or t: 2
Spaces: 2
VS Code running LinqAnyCount.cs: C# LINQ Any(char.IsDigit) returns True and Count(char.IsDigit) counts 5 digits in Austin, TX 78701
Any answers yes or no, Count tells you how many

Any(char.IsDigit) returns True, and Count finds 5 digits and 2 Ts in “Austin, TX 78701”. All(c => c != '!') is the reverse check: no exclamation mark anywhere.

For one exact character, AsSpan().Count(' ') (.NET 8+) counts the 2 spaces without LINQ. Under the hood, LINQ just walks the string like a foreach loop in C# would.

Null-Safe Contains Check (and the Errors to Avoid)

Real input can be empty or null. Calling Contains on a null string throws, so guard it with ?. and ?? false:

string?[] inputs = ["Tyler Brooks", "", null];

foreach (string? s in inputs)
{
    bool hasSpace = s?.Contains(' ') ?? false;
    string shown = s is null ? "null" : $"\"{s}\"";
    Console.WriteLine($"{shown,-15} has a space: {hasSpace}");
}

string? missing = null;
try { Console.WriteLine(missing!.Contains(' ')); }
catch (NullReferenceException) { Console.WriteLine("NullReferenceException"); }

try { Console.WriteLine("Tyler".Contains((string)null!)); }
catch (ArgumentNullException ex) { Console.WriteLine(ex.GetType().Name); }

Output:

"Tyler Brooks"  has a space: True
""              has a space: False
null            has a space: False
NullReferenceException
ArgumentNullException
VS Code running NullSafeContains.cs: C# s?.Contains(' ') ?? false returns False for null and empty strings, plus NullReferenceException and ArgumentNullException
The ?. and ?? pattern returns False for null instead of crashing

“Tyler Brooks” has a space, while the empty string and null both return False. Without the guard, missing.Contains(' ') throws NullReferenceException.

There’s a second trap: passing a null argument to Contains(string) throws ArgumentNullException, caught here with a try-catch block. To test the string itself first, check if a string is not null or empty.

Working on more string checks? These pair well with this one:

Frequently Asked Questions

How do I check if a string contains a character in C#?

Call text.Contains('x') with a char in single quotes. It returns true if the character appears anywhere in the string and false if it doesn’t.

Is String.Contains case-sensitive in C#?

Yes. Contains(char) and Contains(string) do an ordinal, case-sensitive comparison. Pass StringComparison.OrdinalIgnoreCase as the second argument to ignore case.

Why is there no Contains(char) in .NET Framework?

The char overload arrived in .NET Core 2.1, so .NET Framework 4.8 only has Contains(string). Use text.IndexOf('x') >= 0 there. The String.Contains reference on Microsoft Learn lists every overload and version.

How do I check if a string contains any of several characters?

Use text.IndexOfAny(new[] { '@', '#' }) >= 0 on any version, or text.AsSpan().ContainsAny('@', '#') on .NET 8 and later. For a set you reuse often, create a SearchValues<char> once.

How do I count how many times a character appears in a string?

Use LINQ: text.Count(c => c == 'a'). On .NET 8+, text.AsSpan().Count('a') does the same without a lambda.

What happens if I call Contains on a null string?

It throws NullReferenceException. Write text?.Contains('x') ?? false to get false for null instead.