paddyb
paddyb

Reputation: 77

Accessing a struct collection property from within another collection

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?

Upvotes: 4

Views: 135

Answers (3)

Grzegorz Pływacz
Grzegorz Pływacz

Reputation: 166

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.

Upvotes: 1

user287107
user287107

Reputation: 9417

"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

Upvotes: 1

Jason Evans
Jason Evans

Reputation: 29186

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.

Upvotes: 2

Related Questions