Using return in a foreach statement

Hello,

I faced a strange (somehow resolved) problem with an exception being thrown without much explanations. I'd like to know if someone can point me to the article or specification talking of this behavior, since I didn't find anything yet and am pretty curious about it.

I was messing around with the NETCF2 PocketOulook API and wrote a simple method to retrieve a PimItem in a collection given an itemId which looked like this:

public PimItem GetItem(PimItemCollection collection, int itemId) {
foreach (PimItem item in collection) {
int iId = int.Parse(item.ItemId.ToString());
if (iId == itemId) return item;
}
return null;
}

When the itemId was correct for the collection and the corresponding PimItem was found, if I tried to later update it using the PimItem.Update() method, I kept getting the following exception: "InvalidOperationException: Can't modify the item collection inside a foreach statement". I first thought I had put my Update() call within such a statement. However after spending lots of time debugging my application (my actual code is much more complicated), this was definitely not the case. I then started hunting for every foreach statement I had used even if it had no obvious link with my Update() problem.

It appeared that changing the code of my GetItem() function to something a bit less "return-breaking-loop-dirty", the exception would not be thrown:

public PimItem GetItem(PimItemCollection collection, int itemId) {
int i = 0;
PimItem rItem = null;
while(i < collection.Count && rItem == null) {
int iId = int.Parse(collection[ i ].ItemId.ToString());
if (iId == itemId) {
rItem = collection[ i ];
}
i++;
}
return rItem;
}

With this new implementation, everything works correctly, and the same Update() call I used on my returned PimItem does not throw that InvalidOperationException anymore.

Fine, but still, can someone tell my why returning the item within the foreach loop would throw such an exception

According to the MSDN library, various articles on the web and even the C# Ecma-334 specification §15.8.4, I understood (correct me if I'm wrong) that the foreach CIL conversion should include some kind of finally statement where the hidden IEnumerator object used to loop on the collection is disposed. I have the (very strange) feeling that using a return statement while iterating on a PimItemCollection with the foreach keyword ignores the disposal of this enumerator, thus preventing the manipulation of the collection and its items.

Could someone explain this to me (or negate my "finally statement not being executed" theory)



Answer this question

Using return in a foreach statement

  • AndrewLuiHK

    That sounds like a bug in enumerator implementation. Please file a bug report on connect.microsoft.com. Please attach simple project to reproduce the issue and describe expected and actual behavior. Also please specify the issue is in Windows Mobile (this assembly is not a part of NETCF) so it would be routed to appropriate team.



  • James S. Jan

    Thanks for your reply Ilya. Seems like I have been unable to explain the problem properly though... The foreach is used as read only, but the problem is that having used a foreach without reading all items (break or return when you find wat you look for) seems to leave the collection in a "bad state", but using a read-only while loop doesn't :-o The next time you try to create a new item, after the foreach is done, by calling the AddNew factory on the collection you get an error saying it's not allowed while in a foreach loop.

    OutlookSession session = new OutlookSession();

    //foreach (Appointment app in session.Appointments.Items)

    //{

    // if (app.End > app.Start)

    // break;

    //}

    int i = 0;

    Appointment item = null;

    AppointmentCollection collection = session.Appointments.Items;

    while (i < collection.Count)

    {

    item = collectionIdea;

    if (item.End > item.Start)

    break;

    i++;

    }

    //Throws exception if foreach was used instead of while

    Appointment appointment = session.Appointments.Items.AddNew();



  • ChangLuo

    I have had the exact same issue. Seems like using foreach with the AppointmentCollection doesn't work properly and IMHO this is a bug - you should be able to return or break out of a for each statement without leaving it in a bad state. Using the same foreach code with the InTheHand library foreach works like a charm. I worked around it in the same way as you did...



  • Frances83

    Items obtained in foreach statements are read only:

    http://msdn2.microsoft.com/en-us/library/ttw7t8t6(VS.80).aspx

    The foreach statement repeats a group of embedded statements for each element in an array or an object collection. The foreach statement is used to iterate through the collection to get the desired information, but should not be used to change the contents of the collection to avoid unpredictable side effects.

    In this particular case I would guess there's really no collection of items as you’re using foreach. As you enumerating through items data is probably fetched from the native layer and presented to you in a form of PimItem. That single PimItem probably references enumerator so as you exit foreach loop enumerator actually remains. It’s a cursor model well know from databases, e.g. DataReader class.



  • Using return in a foreach statement