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.
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;
}
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.