Reputation: 39078
I wanted to remove all elements in a collection and since I didn't see .RemoveAll
or such I went with the code below.
int number = addressBook.Items.Count;
System.Collections.IEnumerator enumerator = ...
while (enumerator.MoveNext())
{
target = enumerator.Current as Outlook.ContactItem;
target.Delete();
}
However, I noticed that the number of remaining elements in the collection is roughly the half for each run of the program. My conclusion was that .Delete()
skips to the next element by itself, meaning that .MoveNext()
i nthe condition for the loop jumps to the elements after the next.
So I tried to reset the enumerator as follows.
int number = addressBook.Items.Count;
System.Collections.IEnumerator enumerator = ...
while (enumerator.MoveNext())
{
target = enumerator.Current as Outlook.ContactItem;
target.Delete();
enumerator.Reset();
}
However, as I checked the .Count
I saw that the number of elements remaining was still 1 after the last element being deleted and the enumerator being reset. And of course I got an exception thrown in my face.
What am I missing in this picture? I know it's not a bug because that would be reported and resolved eons ago...
Upvotes: 1
Views: 432
Reputation: 4189
I expect that using the Enumerable.Reverse extension would a welcome improvement to while...Last().Delete()
solution. Here's what that might look like:
foreach(var target in addressBook.Items
.OfType<Outlook.ContactItem>()
.Reverse())
{
target.Delete();
}
I don't use Reverse
that often; in fact, I don't recall ever using it in production code but I haven't had to inter-operate with Outlook either. The benefit here is that you're not repeatedly traversing the entirety of the list-remainder after each item deletion like the while...Last().Delete()
method does. There may not be much (any?) difference in performance when the item lists are small, but if the traversal is expensive (and I imagine that it is since this likely comes down to COM iterop calls under the hood) or if the lists are large, then the performance difference could be significant. The only way to know for sure is to test/profile.
Upvotes: 1
Reputation: 39007
Two things.
You should be able to delete all the items with an enumerator as long as you re-instantiate it after deleting each item. Using LINQ, it should look like:
while (addressBook.Items.Count > 0)
{
addressBook.Items
.OfType<Outlook.ContactItem>()
.Last()
.Delete();
}
According to MSDN, you actually have to delete the items starting with the last one: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook._contactitem.delete.aspx
Upvotes: 1
Reputation: 8071
IEnumerator can be used only as long as the underlying collection remains unchanged. As soon as it changes (e.g. by removing elements from it), the enumerator is irrecoverably invalidated.
You just cannot use one enumerator to browse a collection while modifying it, and Reset is not going to help with that. You just need to create a new enumerator every time. E.g. something like that might work:
while (addressBook.Items.Count > 0)
{
foreach(Outlook.ContactItem target in ...)
{
target.Delete();
break; // <-- !
}
}
Upvotes: 3
Reputation: 18290
Reset is redundant; so much so that it is a requirement in the language spec for iterator blocks to throw an exception on Reset. The correct thing to do is simply dispose and release the old iterator, and call GetEnumerator again. Or better: avoid having to read it twice, since not all data is repeatable.
Refer the following links:
Can't add/remove items from a collection while foreach is iterating over it
When IEnumerator.Reset() method is called?
using IEnumerator to removing items
Hope this help..
Upvotes: 1