All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.plugins.csharp.S6602.html Maven / Gradle / Ivy

There is a newer version: 10.5.0.109200
Show newest version

Why is this an issue?

Both the List.Find method and the Enumerable.FirstOrDefault method can be used to locate the first element that meets a specified condition within a collection. However, for List objects, List.Find may offer superior performance compared to Enumerable.FirstOrDefault. While the performance difference might be negligible for small collections, it can become significant for larger collections. This observation also holds true for ImmutableList and arrays.

It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the performance improvements in .NET 9 have brought FirstOrDefault closer to the performance of collection-specific Find methods in most scenarios.

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 Categories Mean Standard Deviation Allocated

ArrayFirstOrDefault

.NET 8.0

Array

10.515 μs

0.1410 μs

32 B

ArrayFind

.NET 8.0

Array

4.417 μs

0.0729 μs

-

ArrayFirstOrDefault

.NET 9.0

Array

2.262 μs

0.0135 μs

-

ArrayFind

.NET 9.0

Array

3.428 μs

0.0206 μs

-

ArrayFirstOrDefault

.NET Framework 4.8.1

Array

45.074 μs

0.7517 μs

32 B

ArrayFind

.NET Framework 4.8.1

Array

13.948 μs

0.1496 μs

-

ImmutableListFirstOrDefault

.NET 8.0

ImmutableList<T>

83.796 μs

1.3199 μs

72 B

ImmutableListFind

.NET 8.0

ImmutableList<T>

59.720 μs

1.0723 μs

-

ImmutableListFirstOrDefault

.NET 9.0

ImmutableList<T>

81.984 μs

1.0886 μs

72 B

ImmutableListFind

.NET 9.0

ImmutableList<T>

58.288 μs

0.8079 μs

-

ImmutableListFirstOrDefault

.NET Framework 4.8.1

ImmutableList<T>

446.893 μs

9.8430 μs

76 B

ImmutableListFind

.NET Framework 4.8.1

ImmutableList<T>

427.476 μs

3.3371 μs

-

ListFirstOrDefault

.NET 8.0

List<T>

14.808 μs

0.1723 μs

40 B

ListFind

.NET 8.0

List<T>

6.040 μs

0.1104 μs

-

ListFirstOrDefault

.NET 9.0

List<T>

2.233 μs

0.0154 μs

-

ListFind

.NET 9.0

List<T>

4.458 μs

0.0745 μs

-

ListFirstOrDefault

.NET Framework 4.8.1

List<T>

57.290 μs

1.0494 μs

40 B

ListFind

.NET Framework 4.8.1

List<T>

18.476 μs

0.0504 μs

-

Glossary

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

// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == 1;
private readonly static Predicate<int> ConditionPredicate = static x => x == 1;
private List<int> list;
private ImmutableList<int> immutableList;
private int[] array;
public const int N = 10_000;

[GlobalSetup]
public void GlobalSetup()
{
    list = Enumerable.Range(0, N).Select(x => N - x).ToList();
    immutableList = ImmutableList.CreateRange(list);
    array = list.ToArray();
}

[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public int ListFirstOrDefault() =>
    list.FirstOrDefault(ConditionFunc);

[BenchmarkCategory("List<T>"), Benchmark]
public int ListFind() =>
    list.Find(ConditionPredicate);

[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public int ImmutableListFirstOrDefault() =>
    immutableList.FirstOrDefault(ConditionFunc);

[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public int ImmutableListFind() =>
    immutableList.Find(ConditionPredicate);

[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public int ArrayFirstOrDefault() =>
    array.FirstOrDefault(ConditionFunc);

[BenchmarkCategory("Array"), Benchmark]
public int ArrayFind() =>
    Array.Find(array, ConditionPredicate);

Hardware configuration:

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
  [Host]               : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256
  .NET 8.0             : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET 9.0             : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256




© 2015 - 2025 Weber Informatics LLC | Privacy Policy