Itear a través de un diccionario al modificar sus valores (pero no sus claves)

Hay algunas otras preguntas que tocan este tema, pero ninguna parece abordarlo (o entonces, son demasiado complejos para que yo las entienda...)

Tengo un:

Private MCDevices As New Dictionary(Of IPAddress, MCDeviceInfo)

En esto Dictionary, el IPAddress son las llaves (obviamente), y MCDeviceInfo son Classes definido en otro lugar. Así que es básicamente un Dictionnary(Of IPAddress, Class).

Quiero hacer un For ... Next el bucle a través del MCDevices Dictionary, y en ese bucle, cambio el contenido del MCDeviceInfo Classes: en el bucle, cambio el valores de la KVP, pero no IPAddress se añade, cambia o se elimina dentro del bucle de cualquier manera, y lo comprobé doblemente.

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

Aún así, alcanzando el Next, entiendo:

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

El condicional podrá aquí es muy optimista, ya que el código se detiene allí...

¿Hay alguna manera de manejar esto? ¿Por qué no puedo cambiar los Valores mientras las Claves estén intactas?

Pregunta hecha hace 3 años, 4 meses, 26 días - Por codealchemyf00f


4 Respuestas:

  • Tú. puede cambiar los valores. Considere el siguiente código y comparelo a su código secreto:

    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
    

    Productos:

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

    Respondida el Dec 19, 2020 a las 21:20 - por codervoyager

    Votos positivos: 0 | Votos negativos: 0

  • Basado en la entrada de @AndrewMorton, @Jimi y @JayV y @jmcilhinney, ahora creo que reemplazando los Valores, así que sustitución el MCDeviceInfo Classes, y no simplemente cambiar sus propiedades, es probablemente el problema.

    Además, ahora sé que lo siguiente hace no trabajo:

    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.
    

    Pero lo siguiente ¿Sí? trabajo (en esencia una traducción de lo que @Justin R. escribió en su respuesta vinculada a por @JayV en él comenta mi pregunta inicial):

    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
    

    Resulta que For Each está perfectamente bien, tanto como un regular For...Next Lo es. En la segunda versión, el bucle está sobre una lista totalmente separada, keys.

    Respondida el Dec 19, 2020 a las 21:28 - por scriptwarrior

    Votos positivos: 0 | Votos negativos: 0

  • El error System.InvalidOperationException: 'Collection was modified; enumeration operation may not continue' se produce cuando intentas modificar la colección (en este caso el diccionario) mientras estás iterando sobre ella. En este caso, estás intentando modificar el valor de las instancias de MCDeviceInfo dentro del bucle For Each, lo cual está causando el error.

    Una forma de resolver este problema es crear una nueva instancia de Dictionary(Of IPAddress, MCDeviceInfo) donde almacenar los nuevos valores actualizados. Podrías crear una lista de tuplas con las claves originales y los nuevos valores, y luego recorrer esa lista para actualizar el diccionario original una vez finalizada la iteración.

    Aquí te dejo un ejemplo de cómo podrías hacerlo:

    vb
    Dim updatedMCDevices As New Dictionary(Of IPAddress, MCDeviceInfo)
    
    For Each kvp As KeyValuePair(Of IPAddress, MCDeviceInfo) In MCDevices
        ' Modificar el contenido de MCDeviceInfo sin modificar la clave
        Dim updatedMCDeviceInfo As MCDeviceInfo = ModificarMCDeviceInfo(kvp.Value)
    
        updatedMCDevices.Add(kvp.Key, updatedMCDeviceInfo)
    Next
    
    ' Actualizar el diccionario original con los nuevos valores
    MCDevices = updatedMCDevices
    
    ' Función para modificar MCDeviceInfo
    Private Function ModificarMCDeviceInfo(originalMCDeviceInfo As MCDeviceInfo) As MCDeviceInfo
        ' Lógica para modificar el contenido del MCDeviceInfo
    End Function
    

    De esta manera, no estarás modificando el diccionario original mientras lo estás recorriendo, evitando así el error de modificación de la colección durante la iteración.

    Respondida el Dec 20, 2020 a las 21:20 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • El error "Collection was modified; enumeration operation may not continue" ocurre porque estás modificando el diccionario mientras iteras sobre él. Cuando modificas el diccionario, la colección subyacente cambia, lo que invalida el enumerador que estás utilizando para iterar.

    Para evitar este error, puedes crear una copia del diccionario antes de iterar sobre él:

    vb
    Dim tempDict As New Dictionary(Of IPAddress, MCDeviceInfo)
    tempDict = MCDevices
    
    For Each kvp As KeyValuePair(Of IPAddress, MCDeviceInfo) In tempDict
        ' Some code changing the contents of the MCDeviceInfo but not the IPAddress
    Next
    

    De esta manera, puedes modificar el diccionario original sin invalidar el enumerador.

    Respondida el Dec 20, 2020 a las 21:21 - por Gemini

    Votos positivos: 0 | Votos negativos: 0