简体   繁体   中英

Iterate through a Dictionary while modifying its Values (but not its Keys)

There are a few other questions that touch this subject, but none seem to address it (or then, they are too complex for me to understand them...)

I have a:

Private MCDevices As New Dictionary(Of IPAddress, MCDeviceInfo)

In this Dictionary , the IPAddress are the keys (obviously), and the MCDeviceInfo are Classes defined elsewhere. So it's basically a Dictionnary(Of IPAddress, Class) .

I want do a For... Next loop through the MCDevices Dictionary , and in that loop, I change the contents of the MCDeviceInfo Classes : in the loop, I change the values of the KVP, but no IPAddress is added, changed nor deleted within the loop in any way, and I double-checked that.

For Each kvp As KeyValuePair(Of IPAddress, MCDeviceInfo) In MCDevices
' Some code changing the contents of the MCDeviceInfo but not the IPAddress
Next

Still, reaching the Next , I get:

**System.InvalidIperationException:** 'Collection was modified; enumeration operation may not continue'.

The conditional may here is very optimistic, as the code just stops there...

Is there a way to handle this? Why can't I change the Values as long as the Keys are untouched?

You can change the values. Consider the following code and compare it to your secret code:

Imports System.Net

Module Module1

    Private MCDevices As New Dictionary(Of IPAddress, MCDeviceInfo)

    Public Class MCDeviceInfo
        Property Name As String
        Property Vegetarian As Boolean

        Public Overrides Function ToString() As String
            Return $"Name: {Name}"
        End Function
    End Class

    Sub Main()
        MCDevices.Add(IPAddress.Parse("1.1.1.1"), New MCDeviceInfo With {.Name = "Burger", .Vegetarian = False})
        MCDevices.Add(IPAddress.Parse("1.1.1.2"), New MCDeviceInfo With {.Name = "Beanburger", .Vegetarian = True})
        MCDevices.Add(IPAddress.Parse("1.1.1.3"), New MCDeviceInfo With {.Name = "Fries", .Vegetarian = True})

        For Each mcd In MCDevices
            If mcd.Value.Vegetarian Then
                mcd.Value.Name &= " Supersize"
            End If
        Next

        Console.WriteLine(String.Join(vbCrLf, MCDevices))
        Console.ReadLine()

    End Sub

End Module

Outputs:

[1.1.1.1, Name: Burger]
[1.1.1.2, Name: Beanburger Supersize]
[1.1.1.3, Name: Fries Supersize]

Based on the input of @AndrewMorton, @Jimi and @JayV and @jmcilhinney, I now think that replacing the Values, so replacing the MCDeviceInfo Classes , and not merely changing their properties, is probably the problem.

Also, I now know that the following does not work:

For Each kvp As KeyValuePair(Of IPAddress, MCDeviceInfo) In MCDevices
  ' Some code that changes the properties of the MCDeviceInfo Classes and even
  ' replaces them although the keys, the IPAddresses, are not touched in any way.
Next ' fails on the Next with the error mentioned in my question.

But the following does work (in essence a translation of what @Justin R. wrote in his answer linked to by @JayV in he comments to my initial question):

Dim keys As List(Of IPAddress) = MCDevices.Keys.ToList
For Each key As IPAddress In keys ' Loop over the IPAddress , the keys of the KVP
  ' Some code that changes the properties of the MCDeviceInfo Classes and even
  ' replaces them.
Next

It turns out a For Each is perfectly OK, as much as a regular For...Next is. In the second version, the loop is over a totally separate list, keys .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM