Runtime: Proposal: ForEach in Linq

Created on 7 Oct 2018  路  13Comments  路  Source: dotnet/runtime

Background:
I have seen many libraries have been Implementing custom extension method for LINQ. Most of the project I have work with has this extension method. Not all projects need all the methods from Libraries like MoreLinq.
Proposal:
My Proposal is to have ForEach extension method as part of LINQ.

api-suggestion area-System.Linq

Most helpful comment

Eric Lippert has an article on the topic.

I do find collection.foreach more natural to write, which is why I like ReSharper's postfix templates. But using those does not require changing LINQ.

All 13 comments

When is enumerable.ForEach(item => { }) better than foreach (var item in enumerable) { }?

Eric Lippert has an article on the topic.

I do find collection.foreach more natural to write, which is why I like ReSharper's postfix templates. But using those does not require changing LINQ.

@svick and @jnm2 It's equally important in IEnumerable because other Linq methods return IEnumerable. It's very much important when you have linq chain and at the end of it you want to do foreach. It looks more readable when you can add foreach at the end of chain.

@hajirazin I think this would be a better solution, then: https://github.com/dotnet/csharplang/issues/101

The nice thing about this is that you don't need a temporary variable if the target is a large computed expression. I have therefore written and used such an extension method. However, I found that the ugliness was not worth it.

In my opinion it is a mistake that List<T> has a ForEach method.

@jnm2 that looks better solution, but as you see for operations like Where, there are extension methods and linq syntax both. I agree in query syntax do makes more sense but in chaining syntax, extension methods looks good. You can name it as Do as well.

public void Do<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
{
    foreach(var item in source)
    {
        action(item);
    }
}  

After adding extention with Do method, code becomes more compact/readable

            Directory.EnumerateFiles(@"c:\tmp")
                .Where(x => !x.Contains(@".cs.")
                .Do(x => File.Move(x, x+ ".txt"));

comparing to:

            var files = Directory.EnumerateFiles(@"c:\tmp")
                .Where(x => !x.Contains(@".cs.");

            foreach (var file in files)
            {
                    File.Move(file, file + ".txt");                
            }

So, it'll be good if we add method Do to linq.

Something missing from this conversation is what happens after the .ForEach. Lets say I want to perform a mutation to each item in a list, then do something with that.

var updatedIds = dataSource.Where(x=> x.IsActive).ForEach(x => {
         x.IsActive = false;
         x.ClaimedBy = me;
}.Select(x => x.Id).ToList();

I can't perform this query later. Besides being inefficient, I've changed the value used by the WHERE clause. The only option with today's syntax is to create a temporary list for the Where step, then do the for-each, then go back to that temporary list to do the select.

var temp = dataSource.Where(x=> x.IsActive).ToList(); // don't forget ToList or this wont' work.

foreach ( var x in temp)
{
         x.IsActive = false;
         x.ClaimedBy = me;
}

var updatedIds = temp.Select(x => x.Id).ToList();

Besides being slightly more useful than a void-returning ForEach, this gives us the ability to exand our ORM support. This can easily be translated into an update-with-output SQL expression. And right now, batch updates is a major hole in ORMs such as EF.

@Grauenwolf How would you translate that query into an SQL expression, when C# doesn't support Expression Trees with statement lambdas like the one you used? (At least not without https://github.com/dotnet/csharplang/issues/158.)

I'm ok with ORMs not immediately supporting it so long as there is a long term plan.

Duplicate of #40409.

Closing, for the reasons already described in that issue.

@eiriktsarpalis i think this is the older issue than the other one, and i see community giving comments and understanding the needs of it. We could achieve even .Where or .Select with foreach but .Do method is syntactical sugar and i've in many projects that people create this extension by them selfs. And if we want to support in ORM, then it should be query terminator like toList, query should be executed and forEach should be performed on that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

noahfalk picture noahfalk  路  3Comments

iCodeWebApps picture iCodeWebApps  路  3Comments

yahorsi picture yahorsi  路  3Comments

Timovzl picture Timovzl  路  3Comments

matty-hall picture matty-hall  路  3Comments