In C #, how does foreach work when an enum container is modified

This seems like it needs to be answered, but the potential cheats I found were asking different things ...

I noticed it works fine ( sourceDirInclusion

is simple Dictionary<X,Y>

)

    foreach (string dir in sourceDirInclusion.Keys)
    {
        if (sourceDirInclusion[dir] == null)
            sourceDirInclusion.Remove(dir);
    }

      

Does this mean that it is foreach

safe to remove items from the collection or am I lucky?

How about if I was adding more items to the dictionary rather than removing it?

The problem I'm trying to solve is that sourceDirInclusion

it is initially populated, but then each value can deposit new words into the dictionary in a second pass. For example, what I want to do is:

foreach (string dir in sourceDirInclusion.Keys)
{
  X x = sourceDirInclusion[dir];
  sourceDirInclusion.Add(X.dir,X.val);
}

      

+3


source to share


4 answers


Short answer: it is not safe.

Long answer: from the IEnumerator<T>

documentation
:

The enumerator remains in effect as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or removing elements, the enumerator is irrevocably invalidated and its behavior is undefined.

Note that the docs state that the behavior is undefined, which means it may or may not work. You should never rely on undefined behavior.

In this case, it depends on the behavior of the enumerated type Keys

, regardless of whether it creates a copy of the list of keys when the enumeration starts. In this particular case , it is known from the docs that the return value from Dictionary<,>.Keys

is a collection that refers to a dictionary:

The one returned is Dictionary<TKey, TValue>.KeyCollection

not a static copy; instead Dictionary<TKey, TValue>.KeyCollection

refers to the keys in the original Dictionary<TKey, TValue>

. Therefore, changes to Dictionary<TKey, TValue>

continue to be reflected in Dictionary<TKey, TValue>.KeyCollection

.



So it should be unsafe to change the dictionary while listing the keys of the dictionary.

You can fix this with one change. Change this line:

foreach (string dir in sourceDirInclusion.Keys)

      

For this:

foreach (string dir in sourceDirInclusion.Keys.ToList())

      

The extension method ToList()

will create an explicit copy of the list of keys, making it safe to modify the dictionary; the "base collection" will be the copy, not the original.

+7


source


If he throws

InvalidOperationException: Message = "The collection was changed, the enumeration operation may not be performed



To avoid adding candidates for deletion to the external list. Then flip it over and remove it from the target container (dictionary).

List<string> list = new List<string>(sourceDirInclusion.Keys.Count);
foreach (string dir in sourceDirInclusion.Keys)
{
    if (sourceDirInclusion[dir] == null)
        list.Add(dir);
}
foreach (string dir in list)
{
    sourceDirInclusion.Remove(dir);
}

      

+3


source


check this out: What's the best way to modify the list in a foreach loop?

In short:

The collection used in foreach is immutable. This is very much by design.

      

As MSDN says:

The foreach statement is used to iterate over a collection to get the information it needs, but cannot be used to add or remove items from the original collection to avoid unpredictable side effects. If you need to add or remove items from the original collection, use a for loop.

UPDATE: You can use a for loop instead:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}

      

0


source


This works because you are looking at sourceDirInclusion.Keys.

However, to be sure of future versions of FrameWork, I recommend that you use sourceDirInclusion.Keys.ToArray () in the foreach statement so you create a copy of the keys you are executing.

However, this won't work:

foreach(KeyValuePair<string, object> item in sourceDirInclusion)
{
    if (item.Value == null)
        sourceDirInclusion.Remove(item.Key);
}

      

Typically, you cannot modify a collection while traversing it, but often you can create a new collection using .ToArray () or .ToList () and navigate around changing the original collection.

Good luck with your search.

0


source







All Articles