Why is this an issue?

Both the List.Find method and IEnumerable.FirstOrDefault method can be used to find the first element that satisfies a given condition in a collection. However, List.Find can be faster than IEnumerable.FirstOrDefault for List objects. For small collections, the performance difference may be minor, but for large collections, it can make a noticeable difference. The same applies for ImmutableList and arrays too.

Applies to

What is the potential impact?

We measured at least 2x improvement in the execution time. For more details see the Benchmarks section from the More info tab.

How to fix it

The Find method is defined on the collection class, and it has the same signature as FirstOrDefault extension method. The function can be replaced in place.

Code examples

Noncompliant code example

int GetValue(List<int> data) =>
    data.FirstOrDefault(x => x % 2 == 0);
int GetValue(int[] data) =>
    data.FirstOrDefault(x => x % 2 == 0);

Compliant solution

int GetValue(List<int> data) =>
    data.Find(x => x % 2 == 0);
int GetValue(int[] data) =>
    Array.Find(data, x => x % 2 == 0);

Resources

Documentation

Benchmarks

Method Runtime Mean StdDev Ratio Allocated

FirstOrDefault

.NET 7.0

5.373 ms

0.1049 ms

1.00

125 KB

Find

.NET 7.0

1.691 ms

0.0334 ms

0.32

85.94 KB

FirstOrDefault

.NET Framework 4.6.2

5.035 ms

0.0421 ms

1.00

125.38 KB

Find

.NET Framework 4.6.2

1.779 ms

0.0107 ms

0.35

86.2 KB

The results were generated by running the following snippet with BenchmarkDotNet:

private List<string> data;
private Random random = new Random();

[Params(1_000)]
public int N { get; set; }

[GlobalSetup]
public void Setup() =>
    data = Enumerable.Range(0, N).Select(x => Guid.NewGuid().ToString()).ToList();

[Benchmark(Baseline = true)]
public void FirstOrDefault()
{
    for (var i = 0; i < N; i++)
    {
        var value = data[random.Next(N - 1)];
        _ = data.FirstOrDefault(x => x == value);   // Enumerable.FirstOrDefault()
    }
}

[Benchmark]
public void Find()
{
    for (var i = 0; i < N; i++)
    {
        var value = data[random.Next(N - 1)];
        _ = data.Find(x => x == value);             // List<T>.Find()
    }
}

Hardware configuration:

BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
  [Host]               : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256
  .NET 7.0             : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
  .NET Framework 4.6.2 : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256