Sending items in a LINQ sequence to a method that

2020-08-10 19:35发布

问题:

Often while I'm dealing with LINQ sequences, I want to send each item to a method returning void, avoiding a foreach loop. However, I haven't found an elegant way to do this. Today, I wrote the following code:

    private StreamWriter _sw;
    private void streamToFile(List<ErrorEntry> errors)
    {
        if (_sw == null)
        {
            _sw = new StreamWriter(Path.Combine
                                    (Path.GetDirectoryName(_targetDatabasePath), "errors.txt"));
        }

        Func<ErrorEntry, bool> writeSelector = 
            (e) => { _sw.WriteLine(getTabDelimititedLine(e)); return true; };

        errors.Select(writeSelector);

        _sw.Flush();
    }

As you can see, I write a lambda function that just returns true, and I realize that the Select method will return a sequence of booleans- I'll just ignore that sequence. However, this seems a little bit noobish and jank. Is there any elegant way to do this? Or am I just misapplying LINQ?

Thanks.

回答1:

First of all, your current code will not work.
Select, and most other LINQ methods, use deferred execution, meaning that they don't actually do anything until you enumerate the results.

In general, you should never use a lambda with side effects in a LINQ query.

To answer your question, you should use a foreach loop.

You're looking for a ForEach extension method; Eric Lippert explains why Microsoft didn't write one.

If you really want to, you can write one yourself:

public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action) {
    if (sequence == null) throw new ArgumentNullException("sequence");
    if (action == null) throw new ArgumentNullException("action");
    foreach(T item in sequence) 
        action(item);
}

//Return false to stop the loop
public static void ForEach<T>(this IEnumerable<T> sequence, Func<T, bool> action) {
    if (sequence == null) throw new ArgumentNullException("sequence");
    if (action == null) throw new ArgumentNullException("action");

    foreach(T item in sequence) 
        if (!action(item))
            return;
}


回答2:

The common consensus is that LINQ is for querying and selection... while you use traditional iterative methods for looping and iteration.

I hate to say this but you would use a traditional foreach loop because you want your linq queries to execute, and you iterate over the resulting IEnumerable. It helps with code readability, and I will admit LINQ is addictive. You want to do everything using Lambdas and deferred execution, but looping should be left to your traditional C# looping methods. This will definitely help with side-effects.



标签: c# .net linq void