VSInsider

Discretionary Development at Microsoft

Have you ever wondered why the Microsoft Base Class Library (BCL) lacks an IEnumerable<T>.ForEach(...) method? Obviously, Microsoft must be careful when extending any API.

The decision to add a method like IEnumerable<T>.ForEach(...) comes down to five things: demand, precedent, advantages, consistency and cost-effectiveness. Let's take a look at each.

The first question is, is there explicit customer demand for a new feature? In the case of IEnumerable<T>.ForEach(...), the answer is yes. The Microsoft Connect bug and feature inquiry site shows plenty of requests for this method, and Microsoft has begun investigating the value.

Another factor is precedent. Does the method exist elsewhere and have proven value? There's already a static implementation of ForEach() on System.Collections. Array and an instance method on System.Coll-e-ctions.Generic.List<T>. And ForEach() is in the Parallel LINQ (PLINQ) API.

Things get murky when you start talking about explicit advantages. IEnumerable<T>.ForEach(...) offers minimal advantage over a foreach statement. Compare the two examples here; one with foreach and one with IEnumerable<T>.ForEach(...), where items is an IEnumerable<string>:

  items.ForEach(item=> 
   {Console.WriteLine(item);});
  foreach (string item in items) 
    { Console.Write(item); }

Yes, if you use a lambda expression (items.ForEach(Console.WriteLine)) rather than a lambda statement, the syntax is more succinct, and still readable. But for a statement block with multiple statements, it's likely that most developers would prefer the foreach statement.

The next issue is consistency, and here the outlook is less clear. Typically, the exception methods provided by System.Linq.Enumerable return a collection of objects -- generally IEnumerable<T>. This is critical because it enables deferred execution, delaying query execution until an explicit call for a result is needed. In the following example, none of the lambda expressions is evaluated until the call to Count():

  items = items.Where(
   item => item == item.ToString());
  items = items.Where(
    item => item.Length > 0);
  items = items.OrderBy(item=>item);
  IEnumerable<char> firstLetters = 
    items.Select(item => item[0]);
  int count = 
    firstLetters.Distinct().Count();

Until the call to Count(), the criteria expressed by each lambda expression is combined together into one big query, rather than executed (or evaluated) piecemeal.

Although having IEnumerable<T>.ForEach(...) return an IEnumerable<T> collection would be consistent with the earlier, well-established pattern, it would also likely lead to runtime coding errors because statements like items.ForEach(Console.WriteLine) wouldn't write anything out. Rather, the execution of the WriteLine statement would be delayed until the expression was evaluated. Unless the result of ForEach() was assigned, it would never be evaluated, making for a line of code that appeared to be exactly what was needed but, in actuality, did nothing.

In conclusion, it's likely that consistency with other System.Linq.Enumerable methods should probably be dropped, as it was for List<T>.ForEach(). That means losing support for having a fluent API and its characteristic method chaining.

Last comes the question of cost. Implementing IEnumerable<T>.ForEach(...) is fairly trivial, as shown here:

  public static void ForEach<T>(
   this IEnumerable<T> collection, 
   Action<T> action)
 {
   foreach (T item in collection)
   {
     action(item);
   }
 }

However, without testing, it's likely that the consequence of deferred execution would go unnoticed. The method is small and clear enough that many would naively assume they could write it correctly without unit testing it. And, even when returning IEnumerable<T>, they would. However, once testing was in place to demonstrate that deferred execution would yield very misleading code, it would be discovered that the design needed more thought.

Also, because the method is easy to implement, it is perhaps not as crucial to include in the BCL -- a developer could implement it themselves.

IEnumerable<T>.ForEach(...) is an example of a relatively trivial API with several factors in its favor. But Microsoft must evaluate the issues very closely. We should pay similar attention to our own API design and the code we write. Will the company provide IEnumerable<T>.ForEach(...) in the Microsoft .NET Framework 5? No doubt Microsoft is considering it.

About the Author

Mark Michaelis (http://IntelliTect.com/Mark) is the founder of IntelliTect and serves as the Chief Technical Architect and Trainer. Since 1996, he has been a Microsoft MVP for C#, Visual Studio Team System, and the Windows SDK and in 2007 he was recognized as a Microsoft Regional Director. He also serves on several Microsoft software design review teams, including C#, the Connected Systems Division, and VSTS. Mark speaks at developer conferences and has written numerous articles and books - Essential C# 5.0 is his most recent. Mark holds a Bachelor of Arts in Philosophy from the University of Illinois and a Masters in Computer Science from the Illinois Institute of Technology. When not bonding with his computer, Mark is busy with his family or training for another triathlon (having completed the Ironman in 2008). Mark lives in Spokane, Washington, with his wife Elisabeth and three children, Benjamin, Hanna and Abigail.

comments powered by Disqus

Featured

  • .NET 11 Preview 5 Focuses on Performance, Productivity and Safer Code

    .NET 11 Preview 5 focuses on under-the-hood runtime performance gains, streamlined APIs and language features that reduce boilerplate, plus built‑in security checks and incremental ASP.NET Core and EF Core improvements aimed at everyday developer productivity.

  • VS Code 1.124 Focuses on Agent Autonomy and Parallel Sessions

    Microsoft's June 2026 VS Code update turns on Autopilot by default and adds background sending for agent sessions.

  • Developing Agentic Systems in .NET: From Concept to Code

    ZioNet founder Alon Fliess previews his Visual Studio Live! San Diego session on building true agentic systems in .NET -- covering the cognitive loop, MCP tool integration, multi-agent orchestration and enterprise hosting and governance with the Microsoft Agent Framework.

  • Mastering AI Development and Building AI Apps with GitHub Copilot

    Two Microsoft experts explain how GitHub Copilot is evolving from a coding assistant into a broader platform for building, customizing and testing AI-powered developer workflows.

Subscribe on YouTube