tags:

views:

117

answers:

2

Hello all. 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.

+2  A: 

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;
}
SLaks
Thanks for the answer. I have a couple questions:Let's say, for some crazy reason, I called the Count() method on the sequence of booleans that the Select() method returns. This would force the sequence to be enumerated, and the code should work. My question is then: why should we never use lambdas with side effects in a LINQ query? [EDIT: Thanks for the link! I'll check that out.]
generalt
Correct; calling `Count()` will force the sequence to be enumerated.
SLaks
You should never mix side effects with LINQ because your lambdas can be run multiple times. It can be very difficult to know exactly how many times a lambda will be executed; your lambdas should be idempotent.
SLaks
Why not use `Predicate<T>` instead of `Func<T, bool>`?
Gabe
@Gabe: All LINQ methods use the `Func` and `Action` delegates; I'm following the pattern. However, `Predicate<T>` would also work.
SLaks
@SLaks - I have to admit, I googled idempotent:"Idempotence describes the property of operations in mathematics and computer science that means that multiple applications of the operation do not change the result."Interesting. This seems to be applicable to the functional programming paradigm, from what I've read about functional programming.I've been pondering side effects recently. Saving a problem is a "stateful" operation because a different file can be saved each time, and therefore the result is different? I was having a hard time defining a frame of reference for "side effect".
generalt
@SLaks - Also, I don't know much computer science because my background is in chemical engineering, but I want to absorb as much about it as possible.
generalt
Writing to disk is a side effect because it affects something outside the method. (In this case, a file on disk) LINQ brings C# much closer to functional programming.
SLaks
+1  A: 

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.

masenkablast