programming
linq
foreach
duplicates
csharp

Linq style For Each

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

People often ask for a "LINQ style ForEach" because they want loop code to look more fluent. The important distinction is that LINQ is designed for querying and transforming sequences, while foreach is the normal C# tool for side effects.

LINQ Does Not Provide ForEach for IEnumerable

There is no built-in ForEach extension method for IEnumerable<T> in standard LINQ. That is intentional. LINQ focuses on returning sequences, not on executing imperative actions against each element.

You can write this:

csharp
1var names = new[] { "Ada", "Linus", "Grace" };
2
3foreach (var name in names)
4{
5    Console.WriteLine(name);
6}

And that is usually the best answer. It is direct, obvious, and side-effect friendly.

By contrast, LINQ is meant for tasks like filtering or projection:

csharp
1var upper = names
2    .Where(name => name.Length > 3)
3    .Select(name => name.ToUpperInvariant())
4    .ToList();

That query builds a new result. It is not just "loop and do something".

List<T>.ForEach Exists, But It Is Not LINQ

Sometimes people see List<T>.ForEach and assume it is part of LINQ. It is not. It is a method on List<T>.

csharp
var names = new List<string> { "Ada", "Linus", "Grace" };

names.ForEach(name => Console.WriteLine(name));

This works, but only because the source is a List<string>. If you have an IEnumerable<string> from a LINQ query, that method is not available unless you first materialize the sequence:

csharp
1names
2    .Where(name => name.Length > 3)
3    .ToList()
4    .ForEach(name => Console.WriteLine(name));

That extra ToList can be fine, but it means you are paying to allocate a list just to run side effects. Often a plain foreach is clearer and cheaper.

Why foreach Is Usually Better

foreach communicates intent well:

  • iterate now
  • perform side effects
  • no hidden materialization

It also avoids mixing query style with mutation style. Once you start using LINQ methods purely to trigger actions, the code becomes harder to reason about because LINQ's usual mental model is "describe data flow, then enumerate later".

A readable pattern is:

csharp
1var filtered = names.Where(name => name.StartsWith("G"));
2
3foreach (var name in filtered)
4{
5    Console.WriteLine(name);
6}

This keeps the transformation and the action separate.

If You Really Want an Extension Method

You can write your own ForEach extension for IEnumerable<T>:

csharp
1using System;
2using System.Collections.Generic;
3
4public static class EnumerableExtensions
5{
6    public static void ForEach<T>(
7        this IEnumerable<T> source,
8        Action<T> action)
9    {
10        foreach (var item in source)
11        {
12            action(item);
13        }
14    }
15}

Usage:

csharp
var numbers = Enumerable.Range(1, 3);
numbers.ForEach(n => Console.WriteLine(n));

This is valid code, but many teams avoid it because it encourages side effects in places where developers expect LINQ-like purity. It also hides enumeration, which matters if the sequence is expensive or lazy.

Deferred Execution Is the Real Subtlety

LINQ queries are often lazy. That means the query does nothing until you enumerate it.

csharp
var query = Enumerable.Range(1, 5)
    .Where(n => n % 2 == 0)
    .Select(n => n * 10);

At this point, no numbers have been processed yet. The work happens when you loop:

csharp
1foreach (var n in query)
2{
3    Console.WriteLine(n);
4}

That is another reason foreach fits naturally with side effects. It is the moment where enumeration actually occurs.

Common Pitfalls

  • Thinking List<T>.ForEach is a LINQ operator. It is a list method.
  • Converting every sequence to List<T> only to call ForEach, which adds unnecessary allocation.
  • Hiding side effects inside query operators such as Select. That makes code misleading.
  • Forgetting that LINQ queries are often lazy, so nothing happens until enumeration.
  • Writing a custom ForEach extension without considering whether the team wants that style.

Summary

  • Standard LINQ does not include ForEach for IEnumerable<T>.
  • The normal C# tool for per-item side effects is foreach.
  • 'List<T>.ForEach exists, but it is not a LINQ feature and only works on lists.'
  • A custom extension method is possible, but it can blur the difference between querying and side effects.
  • In most codebases, foreach remains the clearest and most idiomatic answer.

Course illustration
Course illustration

All Rights Reserved.