4

I have a struct that I need to store in a collection. The struct has a property that returns a Dictionary.

public struct Item
{
    private IDictionary<string, string> values;
    public IDictionary<string, string> Values
    {
        get
        {
            return this.values ?? (this.values = new Dictionary<string, string>());
        }
    }
}

public class ItemCollection : Collection<Item> {}

When testing I've found that if I add the item to the collection and then try to access the dictionary the structs values property is never updated.

 var collection = new ItemCollection { new Item() }; // pre-loaded with an item
 collection[0].Values.Add("myKey", "myValue");
 Trace.WriteLine(collection[0].Values["myKey"]); // KeyNotFoundException here

However if I load up the item first and then add it to a collection the values field is maintained.

 var collection = new ItemCollection();
 var item = new Item();
 item.Values.Add("myKey", "myValue");
 collection.Add(item);
 Trace.WriteLine(collection[0].Values["myKey"]); // ok

I've already decided that a struct is the wrong option for this type, and when using a class the issue doesn't occur, but I'm curious what's different between the two methods. Can anybody explain what's happening?

paddyb
  • 77
  • 7

3 Answers3

2

Change your struct to be a class instead:

public class Item {}

Now I'm not that familiar with the innards, but a struct is a value type and a class is reference type, and I believe that there is logic in there which explains why you get an exception when running the first code example.

I'm hoping someone can step in an give you a more thorough answer, but for now, just change the struct to a class.

Jason Evans
  • 28,906
  • 14
  • 90
  • 154
  • As you can see here (http://stackoverflow.com/questions/521298/when-to-use-struct-in-c), I think having a struct for this situration is not ideal. – Cybermaxs Sep 19 '12 at 09:46
1

"Item" is a value item. that means, if you access the collection, a copy is made. all operations are done on that copy

 collection[0].Values.Add("myKey", "myValue");

here of the first item a copy is made, then the Value get accessor creates a new instance, which is stored in the copy, and then the item is added. then the copy is destroyed.

a workaround would be creating the Values dictionary direct on the creating of the struct

public struct Item
{
    private IDictionary<string, string> values = new Dictionary<string, string>();
    public IDictionary<string, string> Values
    {
        get
        {
            return this.values;
        }
    }
}

if you access the collection, a copy is made. but this copy contains a reference to the same values dictionary like the original one. so the original dictionary is modified

user287107
  • 9,286
  • 1
  • 31
  • 47
1

As it was mentioned before, change struct Item to class Item. When you use struct, an expression collection[0] returns not a reference to object created in new ItemCollection { new Item() } but rather creates a copy of it and gives it to you.