2010-10-19 10 views
7

Tengo un diccionario que está poblado y no tengo control de.Modificar el valor del diccionario es posible. ¿Cuál es el enfoque correcto?

Necesito modificar el valor ¿cómo puedo hacer eso?

He puesto un ejemplo Noddy juntos para explicar el problema

class Program 
{ 
    static void Main(string[] args) 
    { 


     Dictionary<Customer, int> CustomerOrderDictionary = new Dictionary<Customer, int>(); 

     CustomerOrderDictionary.Add(new Customer { Id = 1, FullName = "Jo Bloogs" },3); 

     CustomerOrderDictionary.Add(new Customer { Id = 2, FullName = "Rob Smith" },5); 

     //now I decide to increase the quantity but cannot do the below as value has no setter 

     foreach (var pair in CustomerOrderDictionary) 
     { 
      if(pair.Key.Id==1) 
      { 
       pair.Value = 4;///ERROR HERE 
      } 
     } 
    } 
} 


public class Customer 
{ 
    public int Id { get; set; } 
    public string FullName { get; set; } 
} 

¿Alguna sugerencia? Muchas gracias

Respuesta

8

le sugiero que averiguar qué teclas tienen que modificar primero, y luego iterar sobre esas modificaciones. De lo contrario, terminará modificando una colección mientras la itera, lo que generará una excepción. Así, por ejemplo:

// The ToList() call here is important, so that we evaluate all of the query 
// *before* we start modifying the dictionary 
var keysToModify = CustomerOrderDictionary.Keys 
              .Where(k => k.Id == 1) 
              .ToList(); 
foreach (var key in keysToModify) 
{ 
    CustomerOrderDictionary[key] = 4; 
} 
+0

Hola, muchas gracias que funcionó. Trataré de ver si eso funciona con el código real. Gracias – user9969

6

El problema aquí es que el par está escrito a KeyValuePair, que es un objeto de solo lectura y no se puede modificar. Además, la colección KeyValuePair es una forma de ver el contenido del diccionario (sin cambiarlo).

Lo que quiere hacer aquí es simplemente modificar el diccionario directamente. El Key en el KeyValuePair se puede utilizar para actualizar la misma entrada en el diccionario.

if(pair.Key.Id==1) { 
    CustomerOrderDictionary[pair.Key] = 4; 
} 

EDITAR

Como Jon señaló la asignación invalidará el iterador. La ruta más simple, pero ineficiente, es copiar el enumerador al comienzo del ciclo.

foreach (var pair in CustomerOrderDictionary.ToList()) 
+2

Excepto que modificando el diccionario en valida el iterador :( –

+0

hola, gracias por su respuesta. intentado por doest no funciona. ¿Estás poniendo lo anterior en un ciclo? si no, ¿qué es el par? perdón por ser tonto aquí – user9969

+0

@Jon oh, sí, se olvidó de esa parte. – JaredPar

0
foreach (Customer customer in customers.Keys) 
{ 
    if (customer.Id == 1) 
     customers[ customer ] = 4; 
} 
0
CustomerOrderDictionary[1] = 4; 
+0

Eso está mal, la clave del diccionario no es una int (ver mi respuesta ...) –

0

Aquí hay una manera de hacerlo (sólo la asignación de una parte del valor ..):

CustomerOrderDictionary[new Customer { Id = 1, FullName = "Jo Bloogs" }]=4 

en cuenta que "1" no es una llave en su diccionario. un Customer es, así que tendrás que usar eso.

Nótese también que Customer deben implementar IEquatable como se explica here

0

Ok, en su ejemplo que estés efectivamente acaba de encontrar la entrada para el objeto Customer con Id = 1 y la actualización del valor asociado. En la práctica, creo que su código probablemente podrá obtener una referencia a su objeto previsto Customer antes de actualizar el valor asociado en el diccionario. Si ese es el caso, entonces no hay necesidad de un bucle.

A continuación se muestra un ejemplo muy simple en el que no se necesita un bucle porque su código ya tiene una referencia a la variable customer1. Si bien mi ejemplo es demasiado simplificado, el concepto es que podría obtener una referencia al objeto Customer deseado a través de otros medios que no sean iterar sobre el diccionario.

static void Main(string[] args) 
    { 
     Dictionary<Customer, int> CustomerOrderDictionary = new Dictionary<Customer, int>(); 

     Customer customer1 = new Customer { Id = 1, FullName = "Jo Bloogs" }; 
     Customer customer2 = new Customer { Id = 2, FullName = "Rob Smith" }; 

     CustomerOrderDictionary.Add(customer1, 3); 

     CustomerOrderDictionary.Add(customer2, 5); 

     // you already have a reference to customer1, so just use the accessor on the dictionary to update the value 
     CustomerOrderDictionary[customer1]++; 
    } 

Si es necesario realizar algún tipo de actualización de múltiples Customer objetos basados ​​en otros criterios, entonces puede que tenga un bucle. El siguiente ejemplo asume que tendrá alguna colección que no sea el diccionario que almacena sus objetos Customer, y que puede usar esa colección de objetos Customer para identificar aquellos cuyo valor asociado en el diccionario debe actualizarse.

static void Main(string[] args) 
    { 
     // presumably you will have a separate collection of all your Customer objects somewhere 
     List<Customer> customers = new List<Customer>(); 

     Customer customer1 = new Customer { Id = 1, FullName = "Jo Bloogs" }; 
     Customer customer2 = new Customer { Id = 2, FullName = "Rob Smith" }; 
     Customer customer3 = new Customer { Id = 3, FullName = "Rob Zombie" }; 

     customers.Add(customer1); 
     customers.Add(customer2); 
     customers.Add(customer3); 

     Dictionary<Customer, int> CustomerOrderDictionary = new Dictionary<Customer, int>(); 

     CustomerOrderDictionary.Add(customer1, 3); 
     CustomerOrderDictionary.Add(customer2, 5); 

     // let's just say that we're going to update the value for any customers whose name starts with "Rob" 
     // use the separate list of Customer objects for the iteration, 
     // because you would not be allowed to modify the dictionary if you iterate over the dictionary directly 
     foreach (var customer in customers.Where(c => c.FullName.StartsWith("Rob"))) 
     { 
      // the dictionary may or may not contain an entry for every Customer in the list, so use TryGetValue 
      int value; 
      if (CustomerOrderDictionary.TryGetValue(customer, out value)) 
       // if an entry is found for this customer, then increment the value of that entry by 1 
       CustomerOrderDictionary[customer] = value + 1; 
      else 
       // if there is no entry in the dictionary for this Customer, let's add one just for the heck of it 
       CustomerOrderDictionary.Add(customer, 1); 
     } 
    } 

Si este no es el caso y la única fuente de Customer objetos que usted tiene disponible es el diccionario en sí, entonces usted tendrá que realizar algún tipo de clonación/copia de esos objetos a una lista separada/matriz antes de iterar sobre el diccionario para la modificación. Ver la respuesta de Jon Skeet para este caso; sugiere utilizar un filtro Where en la propiedad Keys del diccionario y usa el método ToList para crear una instancia List<Customer> por separado a los efectos de la iteración.

1

Aquí es un enfoque alternativo

1) Crear una nueva clase

// wrapper class to allow me to edit a dictionary 
public class IntWrapper 
{ 
    public int OrderCount{ get; set; } 
} 

2) Cambiar esta declaración

Dictionary<Customer, IntWrapper> CustomerOrderDictionary = new Dictionary<Customer, IntWrapper>(); 

3) Asignar la variable de

pair.Value.OrderCount = 4; 
+0

¿Por qué el voto negativo? Funciona para mí ... la apreciación constructiva es apreciada – LamonteCristo

+0

Solo defino un 'Titular ' genérico con un solo campo 'Valor', en lugar de definir una clase para ajustar particularmente 'int'. Además, olvidaste mostrar cómo se deben agregar nuevos contenedores al diccionario. – supercat

+0

Una ventaja que vale la pena mencionar es que cuando se utiliza en escenarios de subprocesos libres múltiples, puede usar un 'ReaderWriterLockSlim' para proteger un diccionario, y solo tiene que adquirir un bloqueo' Writer' cuando cambie el diccionario. Al trabajar con un artículo, es posible que desee adquirir un candado en el artículo, pero eso no interferirá con el acceso de otros al diccionario. – supercat

Cuestiones relacionadas