views:

584

answers:

4

Does anyone know if it is possible to exit a generic ForEach that uses lambda? e.g.

someList.ForEach(sl =>
  {
    if (sl.ToString() == "foo")
        break;

    // continue processing sl here
    // some processing code
  }
);

This code itself won't compile. I know I could use a regular foreach but for consistency I want to use lambda.

Many thanks.

+13  A: 

Sure. But first, note that I recommend against this; I say that a sequence operator should not have a side effect, and a statement should have a side effect. If you're doing something in that ForEach lambda, then make it a statement in the body of a foreach loop rather than making it look like a sequence operator.

That said, here's what you do. First, you write yourself a ForEach that works on arbitrary sequences, not just lists:

public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
{
    foreach(var item in sequence) action(item);
}

And now you write your break like this:

someList
    .TakeWhile(x=>x.ToString() != "foo")
    .ForEach(sl=>
    {/*your action here*/});
Eric Lippert
Bart De Smet did a several `ForEach` type things starting with `if`: http://community.bartdesmet.net/blogs/bart/archive/2009/07/11/bart-s-control-library-not-what-you-think-it-is-part-0.aspx.
Dan
+3  A: 

From MSDN

The following rules apply to variable scope in lambda expressions:

snip

A lambda expression cannot contain a goto statement, break statement, or continue statement whose target is outside the body or in the body of a contained anonymous function.

Don't know if that helps given the code you posted. The relevant quote is from the end of the MSDN article.

cmw
The most relevant and complete reply yet. I like it!
Jason D
A: 

Warning: the code below is not to be taken seriously and is provided for entertainment purposes only!

You can 'simulate' a continue with an early return like this:

Enumerable.Range(1, 20)
          .ToList()
          .ForEach(n =>
                {
                    if (n == 10) return;
                    Console.WriteLine("This is not 10: {0}", n);
                });

That said, I think that side effects within lambda's are a sign that you're doing it wrong. Use a proper foreach instead. Or something like TakeWhile, as Eric kindly demonstrated already.

cfern
A: 

How about this?

        Enumerable.Range(1, 10)
        .Where(x => x % 2 != 0)
        .ToList()
        .ForEach(x => Console.WriteLine(x));
Islam Ibrahim