Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If an Exception is thrown in a List<T>.ForEach, does the iteration stop?

If I have the following code:

List<MyClass> list = GetList();
list.ForEach(i => i.SomeMethod());

and let's say SomeMethod() throws an exception. Does ForEach continue iterating, or does it just stop right there?

If it does terminate, is there any way to get the rest of the items in the collection to run their methods?

like image 376
Jason Avatar asked May 17 '12 06:05

Jason


2 Answers

Yes, if an exception is thrown, the loop exits. If you don't want that behaviour, you should put exception handling into your delegate. You could easily create a wrapper method for this:

public static Action<T> SuppressExceptions<T>(Action<T> action)
{
    return item =>
    {
        try
        {
            action(item);
        }
        catch (Exception e)
        {
            // Log it, presumably
        }
    };
}

To be honest, I would try to avoid this if possible. It's unpleasant to catch all exceptions like that. It also doesn't record the items that failed, or the exceptions etc. You really need to think about your requirements in more detail:

  • Do you need to collect the failed items?
  • Do you need to collect the exceptions?
  • Which exceptions do you want to catch?

It would almost certainly be cleaner to create a separate method which used the normal foreach loop instead, handling errors and collecting errors as it went. Personally I generally prefer using foreach over ForEach - you may wish to read Eric Lippert's thoughts on this too.

like image 66
Jon Skeet Avatar answered Oct 07 '22 13:10

Jon Skeet


It will throw an error. You're also well on your way to reimplementing foreach. How about just:

foreach (var item in list)
{
    try
    {
        // dangerous thing with item
    }
    catch (Exception e)
    {
        // how do you want to log this?
    }
}

This has the benefit of working in most versions of .NET and being obviously side-effectful. Of course, you can put this code directly in the ForEach delegate, but I would only suggest that if it's going to be a method itself (rather than a lambda function).


A reasonable alternative is to create your own ForEachWithCatch extension that captures all the exceptions and sends them back to the caller:

public static IEnumerable<Tuple<T,Exception>> ForEachWithCatch<T>(this IEnumerable<T> items, Action<T> action)
{
    var exceptions = new List<Tuple<T,Exception>>();

    foreach(var item in items)
    {
        try
        {
            action(item);
        }
        catch(Exception e)
        {
            exceptions.Add(Tuple.Create(item, e));
        }
    }

    return exceptions;
}

This sends back an enumerable of each item that failed and it's corresponding exception.

like image 37
yamen Avatar answered Oct 07 '22 13:10

yamen