Freigeben über


Why no ForEach method on IEnumerable interfaces

I was asked many times recently regarding this topic, why not Microsoft introduce an extension method ForEach() on interface IEnumerable<T> ? The answer is: "No, and never".

Here are some strong reasons to not bring this extension method on IEnumerable. I will be discussing the following design considerations in this post.

  • Design pattern
  • Consistency
  • Runtime behavior
  • Performance

Design Pattern

As we know, the C# built-in keyword foreach is a syntactical sugar. While compiling the source code contains foreach statements, the complier searches for the underlying object hierarchal inheritance tree to determine whether this object implements IEnumerable interface, if so, complier transforms foreach statement to call appropriate IEnumerable methods to form the loop (i.e. GetEnumerator, MoveNext etc.) That's to say, foreach keyword is based on the IEnumerable interface design pattern. Additionally, IEnumerable<T> is the generic form of IEnumerable interface (it implements IEnumerable), which improves the runtime type resolving performance by using generic type system, IEnumerable<T> is also the pattern to make foreach keyword work. Imagine adding a method ForEach() to these interfaces, does it look strange?

Consistency

If IEnumerable had ForEach() method, how can we know exactly when we consider foreach keyword or IEnumerable.ForEach() ? Though whichever you take to lead the same actual result but the problem is still obvious – that is, keep the consistency of your code. I also remember there is also a discussion for where to consider using FCL types (e.g. System.String) or language built-in type aliases (e.g. string in C#). This kind of argue never ends, and standardize usages of these kinds of stuff is really very difficult.

Runtime Behavior

You may think there is almost no difference if you would add an extension method ForEach to IEnumerable then use this call instead of foreach keyword, however it’s much tricky than you think. ForEach() may have been defined like this:

void ForEach<T>(Action<T>)

This method takes a parameter that is of type Action<T> , and Action<T> can be a lambda expression; each lambda expression can be converted either to an anonymous delegate, or an expression tree, according to the current calling execution context, it’s possible that the parameter of type Action<T> is translated to specific normal code blocks (which executes immediately) or even the “meaning” of the expression represented by the parameter (which not executes immediately). Specifically, when you had a ForEach() extension method defined on an IEnumerable<T> interface and apply this call on the IQueryable<T> instance or LINQ to SQL object, the ForEach() method will not actually be executed immediately, instead, the meaning of this call will be translated into special code recognized by the attached LINQ Provider on this type; as a result, you may get unexpected outputs. Furthermore, if you had defined ForEach() method on the base type for all LINQ enabled objects (i.e. IEnumerable<T> ). it may override the behavior of the same method on the derived types. For more information, see this blog post: https://ppetrov.wordpress.com/2009/01/22/foreach-method-on-ienumerable/

Performance

You have to aware that if you're using the foreach built-in keyword, the following cases with foreach statements will be optimized by C# compiler when compile the source code into IL.

  • String: foreach (char item in myString) will directly use myString.Length instead of call myString.GetEnumerator().
  • Array: foreach (var item in myArray) will directly use myArray.Length instead of call myArray.GetEnumerator().
  • LINQ-enabled objects: foreach will execute the LINQ query immediately in a LINQ-enabled context (deferred execution).
  • foreach (int item in GetItems()) – the method GetItems() will only be evaluated once.

Conclusion

Consider foreach keyword when possible. The built-in foreach keyword brings different runtime behaviors and compiler optimizations for the enumerable objects. Do not extend IEnumerable with defining a ForEach() extension method – it is dangerous, unverifiable, yet not a good design.

Update:

The answer from C# team is here: https://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx. thanks Dixin Yan to provide this link.

Comments

  • Anonymous
    September 21, 2011
    None of these arguments are convincing

  • Anonymous
    September 23, 2011
    None of these arguments are convincing, the runtime behavior one is flat-out wrong (unless you defined a ForEach<T>(Expression<Action<T>>) overload, and why would you ever do that?), and it's pretty-much refuted by the existence of "Seq.iter" in F#.

  • Anonymous
    September 23, 2011
    What if i plan to use IEnumerable instead of foreach in my code, will increase speed??

  • Anonymous
    September 24, 2011
    @Rabo: Thanks for providing your comments, The example of the runtime behavior in my article is to show why you should not create ForEach<T>(Action<T> action) on IEnumerable as an extension method. Action<T> is possible to be converted into expression trees (Expression<Action<T>>), you may see in LINQ to Objects, all expression tree parameters are defined as delegates (like Select(Func<TResult>), but ASP.NET MVC defines parameters as expression objects (like DisplayFor<T>(Expression<Func<T, TResult>>). Using Expression<> types make your library harder to infer from its usage. The runtime behavior is not a strong reason to not make ForEach method, from the C# team's answer, it's side effect. Please see the last paragraph link to get more info.

  • Anonymous
    September 24, 2011
    @Murugan: It's recommended that you always use foreach, foreach has some compiler optimizations as mentioned above. Using IEnumerable's GetEnumerator() and MoveNext() methods will possibly defeat these optimizations.

  • Anonymous
    November 07, 2013
    I am new to c#. I want to know why there is need of ienumerable as foreach loop is travers on all elements by default.