Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catch exception thrown in foreach condition

I have a foreach loop that breaks during the loop in the condition of the foreach itself. Is there a way to try catch the item that throws the exception and then continue the loop?

This will run a few times until the exception hits and then end.

try {
  foreach(b in bees) { //exception is in this line
     string += b;
  }
} catch {
   //error
}

This will not run at all because the exception is in the condition of the foreach

foreach(b in bees) { //exception is in this line
   try {
      string += b;
   } catch {
     //error
   }
}

I know some of you are going to ask how this is happening so here is this: Exception PrincipalOperationException is being thrown because a Principal (b in my example) cannot be found in GroupPrincipal (bees).

Edit: I added the code below. I also figured out that one group member was pointing to a domain that no longer exists. I easily fixed this by deleting the member but my question still stands. How do you handle exceptions that are thrown inside the condition of a foreach?

PrincipalContext ctx = new PrincipalContext(ContextType.domain);
GroupPrincipal gp1 = GroupPrincipal.FindByIdentity(ctx, "gp1");
GroupPrincipal gp2 = GroupPrincipal.FindByIdentity(ctx, "gp2");

var principals = gp1.Members.Union(gp2.Members);

foreach(Principal principal in principals) { //error is here
   //do stuff
}
like image 834
Jonathan Eckman Avatar asked Sep 21 '12 00:09

Jonathan Eckman


People also ask

How do you handle exceptions in ForEach loop?

ForEach overloads do not have any special mechanism to handle exceptions that might be thrown. In this respect, they resemble regular for and foreach loops ( For and For Each in Visual Basic); an unhandled exception causes the loop to terminate as soon as all currently running iterations finish.

How do you handle errors in parallel ForEach?

When you add your own exception-handling logic to parallel loops, each exception should be catched and saved to a list(Use Concurrent Queue), because each loop will create different exception. So after the loop exists we can wrap all exceptions from the list in a System. AggregateException and throw it.

How do you throw an exception without breaking a for loop in C#?

You have to build a try-catch-block inside your loop and don't throw the exception again. I need the outer try-catch-block as there are chance for exception to occur in the outer part, and also need to throw the exception as I need to log the process.


2 Answers

Almost the same as the answer from @Guillaume, but "I like mine better":

public static class Extensions
{
    public static IEnumerable<T> TryForEach<T>(this IEnumerable<T> sequence, Action<Exception> handler)
    {
        if (sequence == null)
        {
            throw new ArgumentNullException("sequence");
        }

        if (handler == null)
        {
            throw new ArgumentNullException("handler");
        }

        var mover = sequence.GetEnumerator();
        bool more;
        try
        {
            more = mover.MoveNext();
        }
        catch (Exception e)
        {
            handler(e);
            yield break;
        }

        while (more)
        {
            yield return mover.Current;
            try
            {
                more = mover.MoveNext();
            }
            catch (Exception e)
            {
                handler(e);
                yield break;
            }
        }
    }
}
like image 178
John Saunders Avatar answered Sep 27 '22 20:09

John Saunders


Maybe you can try to create a method like that:

    public IEnumerable<T> TryForEach<T>(IEnumerable<T> list, Action executeCatch)
    {
        if (list == null) { executeCatch(); }
        IEnumerator<T> enumerator = list.GetEnumerator();
        bool success = false;

        do
        {
            try
            {
                success = enumerator.MoveNext();
            }
            catch
            {
                executeCatch();
                success = false;
            }

            if (success)
            {
                T item = enumerator.Current;
                yield return item;
            }
        } while (success);
    }

and you can use it this way:

        foreach (var bee in TryForEach(bees.GetMembers(), () => { Console.WriteLine("Error!"); }))
        {
        }
like image 26
Guillaume Avatar answered Sep 27 '22 19:09

Guillaume