S3267 — Loops should be simplified with "LINQ" expressions

Language
C#
Type
Code smell
Severity
Minor

Why is this an issue?

Using explicit loops for filtering, selecting, or aggregating elements can make code more verbose and harder to read. LINQ expressions provide a more concise and expressive way to perform these operations, improving code clarity and maintainability.

Performance Considerations

If the affected code is part of a performance-critical hot path and that the fix would negatively impact performance, you can self-declare the PerformanceSensitiveAttribute in your codebase, or use the one provided by Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers:


[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
public sealed class PerformanceSensitiveAttribute() : Attribute;

[PerformanceSensitiveAttribute]
List<string> Method(IEnumerable<string> collection, Predicate<string> condition)
{
  var result = new List<string>();
  foreach (var element in collection)  // Without the attribute, this would raise an issue
  {
    if (condition(element))
    {
      result.Add(element);
    }
  }
  return result;
}

The rule will respect the AllowGenericEnumeration property:


[PerformanceSensitive("Enumeration", AllowGenericEnumeration = true)]
List<string> Method(IEnumerable<string> collection, Predicate<string> condition) { }

In this case, the rule will not be disabled even if the method is marked with the PerformanceSensitiveAttribute attribute.

How to fix it

Replace explicit loops and conditional blocks with equivalent LINQ expressions.

The issue message names the method that fits the loop: Where or Select when the loop transforms a collection, and Any, All or FirstOrDefault when it only inspects the collection to produce a single result.

Note that All expects the opposite condition of the loop it replaces, so the condition has to be negated. The secondary location of the issue points to the condition to negate.

Use the System.Linq.Async package to enable LINQ operations on IAsyncEnumerable prior to .NET 10. On IAsyncEnumerable the equivalents are named AnyAsync, AllAsync and FirstOrDefaultAsync, so the rule does not suggest Any, All or FirstOrDefault there.

Code examples

Noncompliant code example


List<string> Method(IEnumerable<string> collection, Predicate<string> condition)
{
  var result = new List<string>();
  foreach (var element in collection)  // Noncompliant
  {
    if (condition(element))
    {
      result.Add(element);
    }
  }
  return result;
}

List<string> Method(IEnumerable<MyDto> collection)
{
  var result = new List<string>();
  foreach (var element in collection) // Noncompliant
  {
    var someValue = element.Property;
    if (someValue != null)
    {
      result.Add(someValue);
    }
  }
  return result;
}

void Method(List<string> list)
{
  foreach (var element in list) // Noncompliant
  {
    var someValue = element.Length;
  }
}

async void Method(IAsyncEnumerable<int> collection)
{
  await foreach (var element in collection) // Noncompliant
  {
    if (element is 42)
    {
      Console.WriteLine("The meaning of Life.");
    }
  }
}

bool Method(IEnumerable<string> collection, Predicate<string> condition)
{
  foreach (var element in collection) // Noncompliant
  {
    if (condition(element))
    {
      return true;
    }
  }
  return false;
}

bool Method(IEnumerable<string> collection, Predicate<string> condition)
{
  foreach (var element in collection) // Noncompliant
  {
    if (condition(element))
    {
      return false;
    }
  }
  return true;
}

string Method(IEnumerable<string> collection, Predicate<string> condition)
{
  foreach (var element in collection) // Noncompliant
  {
    if (condition(element))
    {
      return element;
    }
  }
  return null;
}

Compliant solution


List<string> Method(IEnumerable<string> collection, Predicate<string> condition) =>
  collection.Where(x => condition(x)).ToList();

List<string> Method(IEnumerable<MyDto> collection) =>
  collection.Select(x => x.Property).Where(y => y != null).ToList();

void Method(List<string> list)
{
  foreach (var length in list.Select(x => x.Length))
  {
    var someValue = length;
  }
}

async void Method(IAsyncEnumerable<int> collection)
{
  await foreach (var element in collection.Where(x => x is 42))
  {
    Console.WriteLine("The meaning of Life.");
  }
}

bool Method(IEnumerable<string> collection, Predicate<string> condition) =>
  collection.Any(x => condition(x));

bool Method(IEnumerable<string> collection, Predicate<string> condition) =>
  collection.All(x => !condition(x));

string Method(IEnumerable<string> collection, Predicate<string> condition) =>
  collection.FirstOrDefault(x => condition(x));

Resources

Documentation

↑ Back to top