Back to Blog
C#

C# Array Contains: Methods and Performance

c# array contains: Learn how to check if a C# array contains a value using LINQ Contains, Array.IndexOf, and manual loops, with performance and edge-case considerations.

C#arraysLINQContainsperformance
Illustration of a C# array with a highlighted element being checked for existence using the Contains method.

Checking whether a C# array contains a specific value is a frequent operation in real-world code. The most direct approach is the LINQ Contains extension method, but the right choice depends on whether you need the index, how you handle case sensitivity, and how often the check runs. This article covers the common methods, their behavior, and the tradeoffs you should consider when using c# array contains in your projects.

Using LINQ's Contains Method

The simplest way to test for existence is the Contains method from the System.Linq namespace. It works on any type that implements IEnumerable<T>, which includes all single-dimensional arrays. The method returns a bool indicating whether the specified value is present.

using System; using System.Linq; int[] numbers = { 1, 2, 3, 4, 5 }; bool hasThree = numbers.Contains(3); // true bool hasTen = numbers.Contains(10); // false

Contains uses the default equality comparer for the element type. For value types like int, this is a simple value comparison. For reference types, it uses the type's Equals implementation unless the type overrides GetHashCode and Equals. This behavior is consistent with most LINQ methods and is usually what you expect.

Checking Without LINQ: IndexOf and Manual Loops

If you are not using LINQ or you need the position of the element, Array.IndexOf is a direct alternative. It returns the zero-based index of the first occurrence, or -1 if the value is not found.

int[] numbers = { 1, 2, 3, 4, 5 }; int index = Array.IndexOf(numbers, 3); // 2 bool exists = index >= 0;

For a manual loop, you can iterate through the array and break when the element is found. This gives you full control over the comparison logic, which can be useful for custom equality rules.

bool ContainsValue(int[] array, int target) { foreach (int value in array) { if (value == target) return true; } return false; }

The manual loop is more verbose but avoids LINQ overhead, which can matter in tight loops where the check is called millions of times. However, the difference is usually negligible for typical application code.

Case-Insensitive String Matching

For string arrays, the default Contains performs a case-sensitive ordinal comparison. To ignore case, use the overload that accepts an IEqualityComparer<string>, passing StringComparer.OrdinalIgnoreCase.

using System; using System.Linq; string[] fruits = { "Apple", "Banana", "Cherry" }; bool hasBanana = fruits.Contains("banana", StringComparer.OrdinalIgnoreCase); // true

This overload is available in .NET Core 2.0+ and .NET 5+. If you are on an older framework, you can convert the array to a List<string> and use the List.Contains overload, or write a manual loop with string.Equals and the appropriate StringComparison.

Performance and Large Arrays

Both Contains and IndexOf perform a linear scan, so their time complexity is O(n) in the worst case. For a single check on a small array, this is fine. But if you need to perform many existence checks against the same array, consider converting it to a HashSet<T> for O(1) average lookups.

using System; using System.Collections.Generic; using System.Linq; int[] numbers = { 1, 2, 3, 4, 5 }; HashSet<int> set = new HashSet<int>(numbers); bool hasThree = set.Contains(3); // O(1) average

Building the HashSet takes O(n) time and additional memory, but it pays off when you query it repeatedly. For a one-off check, the overhead of building the set is usually not worth it.

Choosing Between Contains and IndexOf

The decision between Contains and IndexOf comes down to whether you need the index. Use Contains when you only care about existence and want the most readable code. Use IndexOf when you need to know where the element is located, for example to remove it or to access adjacent elements. IndexOf also lets you avoid the LINQ dependency if your project does not already use System.Linq.

CriterionContainsIndexOf
Return valueboolint (index or -1)
ReadabilityHighModerate
Requires LINQYesNo
Custom comparerOverload availableOverload available in .NET Core
Best forExistence checksWhen index is needed

For most scenarios, Contains is the clearest expression of intent. If you already have a HashSet or Dictionary, their Contains methods are the natural choice.

Multi-Dimensional Arrays and Edge Cases

Contains does not work directly on multi-dimensional arrays because they do not implement IEnumerable<T>. To check a 2D array, you can flatten it with Cast<int>() and then use LINQ.

using System; using System.Linq; int[,] grid = { { 1, 2 }, { 3, 4 } }; bool hasFour = grid.Cast<int>().Contains(4); // true

This works because Cast<int>() iterates over all elements in row-major order. For jagged arrays (arrays of arrays), you need to check each inner array separately or flatten them with SelectMany.

Another edge case is arrays containing null. The default equality comparer treats null as a valid value, so Contains(null) works for reference types. For value types, null is not a valid argument and will cause a compile-time error unless you use a nullable type.

Finally, remember that Contains uses the type's Equals method. If you have a custom class and want to check by a property rather than reference equality, you need to override Equals and GetHashCode, or provide a custom IEqualityComparer<T> to the Contains overload. This is a common source of bugs when developers expect value-based comparison but get reference-based behavior.