How do i exit a List<string>.ForEach loop when using an anonymous delegate?
Asked Answered
P

12

51

In a normal loop you can break out of a loop using break. Can the same be done using an anonymous delegate?

Example inputString and result are both declared outside the delegate.

blackList.ForEach(new Action<string>(
    delegate(string item)
    {
        if(inputString.Contains(item)==true)
        {
            result = true;
            // I want to break here
        }
    }
));

Edit: Thanks for the replies, I'm actually reading your book at the minute John :) Just for the record i hit this issue and switched back to a normal foreach loop but I posted this question to see if i missed something.

Parlous answered 17/2, 2009 at 14:47 Comment(1)
possible duplicate of List ForEach breakGlabella
S
50

As others have posted, you can't exit the loop in ForEach.

Are you able to use LINQ? If so, you could easily combine TakeWhile and a custom ForEach extension method (which just about every project seems to have these days).

In your example, however, List<T>.FindIndex would be the best alternative - but if you're not actually doing that, please post an example of what you really want to do.

Serotine answered 17/2, 2009 at 14:54 Comment(0)
L
36

There is no loop that one has access to, from which to break. And each call to the (anonymous) delegate is a new function call so local variables will not help. But since C# gives you a closure, you can set a flag and then do nothing in further calls:

bool stop = false;
myList.ForEach((a) => {
  if (stop) {
    return;
  } else if (a.SomeCondition()) {
    stop = true;
  }
});

(This needs to be tested to check if correct reference semantics for closure is generated.)

A more advanced approach would be to create your own extension method that allowed the delegate to return false to stop the loop:

static class MyExtensions {
  static void ForEachStoppable<T>(this IEnumerable<T> input, Func<T, bool> action) {
    foreach (T t in input) {
      if (!action(t)) {
        break;
      }
    }
  }
}
Lott answered 17/2, 2009 at 14:59 Comment(0)
N
17

Do you have LINQ available to you? Your logic seems similar to Any:

bool any = blackList.Any(s=>inputString.Contains(s));

which is the same as:

bool any = blackList.Any(inputString.Contains);

If you don't have LINQ, then this is still the same as:

bool any = blackList.Find(inputString.Contains) != null;

If you want to run additional logic, there are things you can do (with LINQ) with TakeWhile etc

Nightstick answered 17/2, 2009 at 15:3 Comment(3)
I suggest FindIndex instead of Find, as it provides a more general way of detecting "entry not found".Serotine
+1, it looks like the issue here isn't breaking out of .ForEach(), it's finding the right method for the job.Radiolocation
@Jon - true - easier to detect for non-classes. I'll leave as is, though, and +1 yours ;-pNightstick
V
6

I don't think there's an elegant way to do it when using the ForEach method. A hacky solution is to throw an exception.

What's preventing you from doing an old fashioned foreach?

foreach (string item in blackList)
{
    if (!inputString.Contains(item)) continue;

    result = true;
    break;
}
Villose answered 17/2, 2009 at 14:51 Comment(2)
Absolutely nothing, I just want to learn more about delegates and their limitations.Parlous
It's five lines longer! +500%!Ame
L
3

If you want a loop, use a loop.

Action allows for no return value, so there's no way the ForEach function could possibly know that you want to break, short of throwing an exception. Using an exception here is overkill.

Lottery answered 17/2, 2009 at 14:53 Comment(0)
D
2

The only way to "exit" the loop is to throw an exception. There is no "break" style way of exiting the .ForEach method like you would a normal foreach loop.

Derail answered 17/2, 2009 at 14:50 Comment(0)
M
2

The ForEach method is not mean to do this. If you want to know if a collection contains an item you should use the Contains method. And if you want to perform a check on all items in a collection you should try the Any extention method.

Metamerism answered 17/2, 2009 at 14:56 Comment(0)
B
2
bool @break = false;

blackList.ForEach(item =>
 {  
    if(!@break && inputString.Contains(item))
     { @break = true;
       result = true;
     }

    if (@break) return;
    /* ... */
 });

Note that the above will still iterate through each item but return immediately. Of course, this way is probably not as good as a normal foreach.

Brio answered 17/2, 2009 at 15:14 Comment(0)
S
1
    class Program
{
    static void Main(string[] args)
    {
        List<string> blackList = new List<string>(new[] { "jaime", "jhon", "febres", "velez" });
        string inputString = "febres";
        bool result = false;
        blackList.ForEach((item) =>
                              {
                                  Console.WriteLine("Executing");
                                  if (inputString.Contains(item))
                                  {
                                      result = true;
                                      Console.WriteLine("Founded!");
                                  }
                              },
                          () => result);
        Console.WriteLine(result);
        Console.ReadLine();
    }


}
public static class MyExtensions
{
    public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action, Func<bool> breakOn)
    {
        foreach (var item in enumerable)
        {
            action(item);
            if (breakOn())
            {
                break;
            }
        }
    }
}
Spancel answered 17/2, 2009 at 15:4 Comment(0)
N
1

Would this work for you:

bool result = null != blackList.Find( item => inputString.Contains(item)) );
Nib answered 4/4, 2012 at 18:40 Comment(0)
F
1
blackList.ForEach(new Action<string>(
    delegate(string item)
    {
        if(inputString.Contains(item)==true)
        {
            result = true;
            // I want to break here
            return; //add a return here to exit
        }
    }
));
Fabi answered 15/9, 2020 at 15:39 Comment(2)
While this code snippet may be the solution, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.Wentworth
The solution is so simple that it doesn't need explanation imho. Its just return;. I wanted to be sure I was right but since 2 people voted down here I wasn't sure. BUT return; is the RIGHT answer!Knapweed
C
0

if you realy want to exist a loop foreach in a list you could use the exception like this code:

public class ExitMyForEachListException : Exception
{
    public ExitMyForEachListException(string message)
        : base(message)
    {
    }
}
class Program
{
    static void Main(string[] args)
    {
        List<string> str = new List<string>() { "Name1", "name2", "name3", "name4", "name5", "name6", "name7" };
        try
        {
            str.ForEach(z =>
            {
                if (z.EndsWith("6"))
                    throw new ExitMyForEachListException("I get Out because I found name number 6!");
                System.Console.WriteLine(z);
            });
        }
        catch (ExitMyForEachListException ex)
        {
            System.Console.WriteLine(ex.Message);
        }

        System.Console.Read();
    }
}

hope this help to get other point of view.

Consulate answered 19/2, 2016 at 16:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.