Modify Dictionary Value is possible.What is the right approach?
I have dictionary that is populated and I have no control of.
I need to modify the value how can I do that?
I have put a noddy example together to explain the problem
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)
{
开发者_StackOverflow中文版 pair.Value = 4;///ERROR HERE
}
}
}
}
public class Customer
{
public int Id { get; set; }
public string FullName { get; set; }
}
Any suggestions? Thanks a lot
I suggest you work out which keys need modifying first, and then iterate over those modifications. Otherwise you'll end up modifying a collection while you're iterating over it, which will throw an exception. So for example:
// 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;
}
The problem here is that pair is typed to KeyValuePair
which is a readonly object and can't be modified. Additionally the KeyValuePair
collection is a way of viewing the contents of the dictionary (not changing it).
What you want to do here is just modify the dictionary directly. The Key
in the KeyValuePair
can be used to update the same entry in the dictionary.
if(pair.Key.Id==1) {
CustomerOrderDictionary[pair.Key] = 4;
}
EDIT
As Jon pointed out the assignment will invalidate the iterator. The simplest, but ineffecient route, is to copy the enumerator at the start of the loop.
foreach (var pair in CustomerOrderDictionary.ToList())
Here is an alternate approach
1) Create a new class
// wrapper class to allow me to edit a dictionary
public class IntWrapper
{
public int OrderCount{ get; set; }
}
2) Change this declaration
Dictionary<Customer, IntWrapper> CustomerOrderDictionary = new Dictionary<Customer, IntWrapper>();
3) Assign your variable
pair.Value.OrderCount = 4;
foreach (Customer customer in customers.Keys)
{
if ( customer.Id == 1 )
customers[ customer ] = 4;
}
CustomerOrderDictionary[1] = 4;
Here's one way to do that (just the assigning a value part..):
CustomerOrderDictionary[new Customer { Id = 1, FullName = "Jo Bloogs" }]=4
Notice that "1" is not a key in your dictionary. a Customer
is, so you'll have to use that.
Notice also that Customer
Should implement IEquatable
as explained here
Ok, in your example you're effectively just finding the entry for the Customer
object with Id
= 1 and updating the associated value. In practice, I think that your code will likely be able to obtain a reference to your intended Customer
object prior to updating the associated value in the dictionary. If that is the case, then there's no need for a loop.
Below is a very simple example where a loop is not needed because your code already has a reference to the customer1
variable. While my example is overly simplified, the concept is that you could potentially obtain a reference to your desired Customer
object through some means other than iterating over the dictionary.
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]++;
}
If you need to perform some kind of update on multiple Customer
objects based on some other criteria, then you might need a loop. The following example assumes that you'll have some collection other than the dictionary that stores your Customer
objects, and that you can use that collection of Customer
objects to identify the ones whose associated value in the dictionary need to be updated.
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);
}
}
If this is not the case and the only source of Customer
objects that you have available is the dictionary itself, then you'll need to perform some kind of cloning/copying of those objects out to a separate list/array prior to iterating over the dictionary for modification. See Jon Skeet's answer for this case; he suggests using a Where
filter on the dictionary's Keys
property and uses the ToList
method to create a separate List<Customer>
instance for the purpose of iteration.
精彩评论