LINQ
C#
programming
data manipulation
collections

Apply function to all elements of collection through LINQ

Master System Design with Codemia

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

Introduction

In C#, LINQ is primarily about querying and transforming sequences, not performing side effects. That distinction matters when you want to “apply a function to all elements.” If the goal is to create a transformed sequence, LINQ is a good fit. If the goal is to run an action for side effects, a normal loop is often clearer.

Use Select for Transformation

When each input element should become a new output value, Select is the correct LINQ operator.

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5var numbers = new List<int> { 1, 2, 3, 4 };
6var squares = numbers.Select(x => x * x).ToList();
7
8Console.WriteLine(string.Join(",", squares));

This does not mutate the original sequence. It projects each item into a new one.

That is the core LINQ mental model: query and projection first, side effects second.

Remember That LINQ Is Deferred by Default

Many LINQ operators, including Select, use deferred execution. That means the projection does not run until the sequence is enumerated.

csharp
1using System;
2using System.Linq;
3
4var query = Enumerable.Range(1, 3).Select(x =>
5{
6    Console.WriteLine($"Transforming {x}");
7    return x * 10;
8});
9
10Console.WriteLine("Before enumeration");
11foreach (var value in query)
12{
13    Console.WriteLine(value);
14}

This matters because developers sometimes expect the function to run immediately just by writing the LINQ expression.

Use a Loop for Side Effects

If the goal is logging, database writes, mutation, or any action that returns no meaningful value, a foreach loop is usually the right tool.

csharp
1using System;
2using System.Collections.Generic;
3
4var items = new List<string> { "a", "b", "c" };
5
6foreach (var item in items)
7{
8    Console.WriteLine(item.ToUpperInvariant());
9}

This is more explicit than abusing LINQ for side effects and is easier to read in code reviews.

List<T>.ForEach Exists, but Use It Deliberately

If the collection is specifically a List<T>, you can call ForEach.

csharp
1using System;
2using System.Collections.Generic;
3
4var items = new List<int> { 1, 2, 3 };
5items.ForEach(x => Console.WriteLine(x * 2));

This is convenient, but it is not a LINQ operator and does not work on every IEnumerable<T>. It also encourages inline side-effect lambdas that can become hard to debug when they grow.

So if the collection type is not already a List<T>, converting it just to call ForEach is usually a sign that a plain foreach statement would have been clearer.

Transform Then Materialize

A common pattern is applying several pure transformations and then materializing once at the end.

csharp
1using System;
2using System.Linq;
3
4var result = Enumerable.Range(1, 10)
5    .Where(x => x % 2 == 0)
6    .Select(x => x * x)
7    .ToList();
8
9Console.WriteLine(string.Join(",", result));

This is where LINQ is strongest: declarative filtering, mapping, and aggregation.

Avoid Hidden Side Effects in LINQ Chains

LINQ queries that mutate outside state are technically possible, but they are usually a design smell.

csharp
1using System.Collections.Generic;
2using System.Linq;
3
4var source = new[] { 1, 2, 3 };
5var log = new List<int>();
6
7var projected = source.Select(x =>
8{
9    log.Add(x);
10    return x * 10;
11}).ToList();

This works, but it mixes transformation with mutation and depends on enumeration timing. In most production code, a simple loop is clearer and safer.

Choosing the Right Tool

Use this rule:

  • Want new values from old values: Select.
  • Want filtering: Where.
  • Want one combined value: Aggregate, Sum, Count, and similar.
  • Want side effects only: foreach or List<T>.ForEach.

That separation keeps code intent obvious.

Common Pitfalls

  • Using LINQ for side effects and assuming it executes immediately.
  • Forgetting to materialize a deferred query when needed.
  • Calling List<T>.ForEach and thinking it is a general IEnumerable<T> feature.
  • Mutating captured external state inside Select and creating subtle timing bugs.
  • Replacing a straightforward loop with LINQ even when it makes the code less readable.

Summary

  • Use LINQ Select when you want to transform each element into a new value.
  • LINQ queries are often deferred, so execution happens on enumeration.
  • Use foreach for side effects such as logging or mutation.
  • Keep LINQ chains pure and focused on querying or projection.
  • Choose the construct that makes intent clearest, not just the shortest one.

Course illustration
Course illustration

All Rights Reserved.