Why can't I change dictionary values

A

Andrew Falanga

Hi,


As you know, I can't post the actual code. However, what's below is
basically a copy/paste, just cut down to illustrate without the
superfluous code.

The following code shows the problem:

namespace SomeSpace {
private struct Struct1 {
public object val;
public Struct1(object o) {
val = o;
}
}

public class MyClass {
Dictionary<string, Struct1> dict1;

public MyClass() {
dict1 = new Dictionary<string, Struct1>();
dict1.Add("key1", new Struct1(0)); // make the val an int32
dict1.Add("key2", new Struct1(0)); // ditto
}

public void F1() {
// need to fill the dictionary will some meaningful data from
// a config file

F2("myCfgFile.xml");

// now, do more meaningful stuff
}

public void F2(string path) {
// parsing routines using System.XML
// oh, something meaningful is found

dict1["key1"].val = Convert.ToInt32(xmlText); // here's my
error

}
}
}


At the point where it says, "here's my error," I'm getting an error
saying that I cannot modify the return value of Dictionary<string,
Struct1>[] because it is not a variable. What? It is to a variable,
it's an object variable in the structure.

Some digging on Google has brought me to a link that says this can't
be done because structures are allocated on the stack, instead of the
heap. The author of that response said it had to do with the
difference between value objects and reference objects. That's ok,
but I'm still at a loss for why this isn't working. Even the link
that he provided to the C# standard showed code example that looks
similar to what I've got, though not using dictionaries, but works.

Any help is greatly appreciated.

Andy
 
J

Jeff Johnson

As you know, I can't post the actual code. However, what's below is
basically a copy/paste, just cut down to illustrate without the
superfluous code.

That's EXACTLY what we mean when we ask you to post real code. We mean only
RELEVANT code, so good job!
The following code shows the problem:

namespace SomeSpace {
private struct Struct1 {
public object val;
public Struct1(object o) {
val = o;
}
}

public class MyClass {
Dictionary<string, Struct1> dict1;

public MyClass() {
dict1 = new Dictionary<string, Struct1>();
dict1.Add("key1", new Struct1(0)); // make the val an int32
dict1.Add("key2", new Struct1(0)); // ditto
}

public void F1() {
// need to fill the dictionary will some meaningful data from
// a config file

F2("myCfgFile.xml");

// now, do more meaningful stuff
}

public void F2(string path) {
// parsing routines using System.XML
// oh, something meaningful is found

dict1["key1"].val = Convert.ToInt32(xmlText); // here's my
error

}
}
}


At the point where it says, "here's my error," I'm getting an error
saying that I cannot modify the return value of Dictionary<string,
Struct1>[] because it is not a variable. What? It is to a variable,
it's an object variable in the structure.

Some digging on Google has brought me to a link that says this can't
be done because structures are allocated on the stack, instead of the
heap. The author of that response said it had to do with the
difference between value objects and reference objects. That's ok,
but I'm still at a loss for why this isn't working. Even the link
that he provided to the C# standard showed code example that looks
similar to what I've got, though not using dictionaries, but works.

Because the value of your dictionary is a structure, when you access that
value you're actually getting a COPY of the structure, not the actual
structure object that you stored in the dictionary. This is because
structures are considered value types and value types are always passed by
value, not reference. The C# compiler understands this and knows that any
change you might make to a member of the returned structure would be made to
the COPY that you get and not to the original structure as is more than
likely your intention, and as such it disallows this action to save you from
yourself. In this particular case, I would think that the copy of the
structure would still point to the same object in val that the original
structure points to, and therefore changing val would indeed change the
original object. However, as I said, C# is simply designed not to let you do
this.

Someone please correct me if my interpretation is wrong.
 
A

Andrew Falanga

As you know, I can't post the actual code.  However, what's below is
basically a copy/paste, just cut down to illustrate without the
superfluous code.

That's EXACTLY what we mean when we ask you to post real code. We mean only
RELEVANT code, so good job!


The following code shows the problem:
namespace SomeSpace {
  private struct Struct1 {
     public object val;
     public Struct1(object o) {
        val = o;
     }
  }
  public class MyClass {
     Dictionary<string, Struct1> dict1;
     public MyClass() {
        dict1 = new Dictionary<string, Struct1>();
        dict1.Add("key1", new Struct1(0)); // make the val an int32
        dict1.Add("key2", new Struct1(0)); // ditto
     }
     public void F1() {
        // need to fill the dictionary will some meaningful data from
        // a config file
        F2("myCfgFile.xml");
        // now, do more meaningful stuff
     }
     public void F2(string path) {
        // parsing routines using System.XML
        // oh, something meaningful is found
        dict1["key1"].val = Convert.ToInt32(xmlText); // here's my
error
     }
  }
}
At the point where it says, "here's my error," I'm getting an error
saying that I cannot modify the return value of Dictionary<string,
Struct1>[] because it is not a variable.  What?  It is to a variable,
it's an object variable in the structure.
Some digging on Google has brought me to a link that says this can't
be done because structures are allocated on the stack, instead of the
heap.  The author of that response said it had to do with the
difference between value objects and reference objects.  That's ok,
but I'm still at a loss for why this isn't working.  Even the link
that he provided to the C# standard showed  code example that looks
similar to what I've got, though not using dictionaries, but works.

Because the value of your dictionary is a structure, when you access that
value you're actually getting a COPY of the structure, not the actual
structure object that you stored in the dictionary. This is because
structures are considered value types and value types are always passed by
value, not reference. The C# compiler understands this and knows that any
change you might make to a member of the returned structure would be madeto
the COPY that you get and not to the original structure as is more than
likely your intention, and as such it disallows this action to save you from
yourself. In this particular case, I would think that the copy of the
structure would still point to the same object in val that the original
structure points to, and therefore changing val would indeed change the
original object. However, as I said, C# is simply designed not to let youdo
this.

Someone please correct me if my interpretation is wrong.

I think I understand. So, more importantly, how can I alter the
values of the structure in that dictionary?

Andy
 
J

Jeff Johnson

I think I understand. So, more importantly, how can I alter the
values of the structure in that dictionary?

You can't. You'll have to remove it and add a new, altered version under the
same key. You can solve the whole problem by getting rid of the structure
and using a class instead.
 
J

Jackie

I think I understand. So, more importantly, how can I alter the
values of the structure in that dictionary?

Andy

I think the sensible thing to do would be either
dict1["key1"] = new Struct1(Convert.ToInt32(xmlText));

or change Struct1 into a class instead of struct, and use your existing
code.
 
J

Jeff Johnson

I think the sensible thing to do would be either
dict1["key1"] = new Struct1(Convert.ToInt32(xmlText));

Does that work? For some reason I had it in my head that you couldn't simply
assign like that. Maybe I'm confusing that with something else.
 
J

Jackie

I think the sensible thing to do would be either
dict1["key1"] = new Struct1(Convert.ToInt32(xmlText));

or change Struct1 into a class instead of struct, and use your existing
code.

Seems like Jeff was quicker.
 
J

Jackie

I think the sensible thing to do would be either
dict1["key1"] = new Struct1(Convert.ToInt32(xmlText));

Does that work? For some reason I had it in my head that you couldn't simply
assign like that. Maybe I'm confusing that with something else.

I gave it a try now to make sure and it worked.
 
A

Andrew Falanga

I think the sensible thing to do would be either
   dict1["key1"] = new Struct1(Convert.ToInt32(xmlText));
Does that work? For some reason I had it in my head that you couldn't simply
assign like that. Maybe I'm confusing that with something else.

I gave it a try now to make sure and it worked.

Thanks Jeff and Jackie. It's nice to know that I can do it this way.
As I mentioned earlier, this isn't all of the code. The structure
actually contains more objects than just the one I have here. I don't
know if I want to put all of the arguments to a new structure into
another constructor ... so ... I think I'm just going to change it to
a class and call it good.

Thanks again everyone,
Andy
 
P

Peter Duniho

Jeff said:
I think the sensible thing to do would be either
dict1["key1"] = new Struct1(Convert.ToInt32(xmlText));
Does that work? For some reason I had it in my head that you couldn't
simply
assign like that. Maybe I'm confusing that with something else.
I gave it a try now to make sure and it worked.

Good to know, thanks.

Just to be complete:

Calling Add() with a key that already exists will fail with an
exception. But using the indexer on the type won't, even if the key
already is present (the existing value is replaced with the new one).

Pete
 
J

Jeff Johnson

I think the sensible thing to do would be either
dict1["key1"] = new Struct1(Convert.ToInt32(xmlText));
Does that work? For some reason I had it in my head that you couldn't
simply
assign like that. Maybe I'm confusing that with something else.
I gave it a try now to make sure and it worked.

Good to know, thanks.

Just to be complete:

Calling Add() with a key that already exists will fail with an exception.
But using the indexer on the type won't, even if the key already is
present (the existing value is replaced with the new one).

Maybe I'm thinking of something in Windows Forms, like a tree view or a list
view where you can't simply change a node/list item by just replacing it;
you have to remove and re-add.
 
J

Jackie

Maybe I'm thinking of something in Windows Forms, like a tree view or a list
view where you can't simply change a node/list item by just replacing it;
you have to remove and re-add.

I gave this a try now:

treeView1.Nodes[0] = new TreeNode("Replaced node");
listView1.Items[0] = new ListViewItem("Replaced item");

TreeView: New node is inserted.
ListView: Existing item is replaced.

I wonder why it works like that.
 
J

Jeff Johnson

Maybe I'm thinking of something in Windows Forms, like a tree view or a
list
view where you can't simply change a node/list item by just replacing it;
you have to remove and re-add.

I gave this a try now:

treeView1.Nodes[0] = new TreeNode("Replaced node");
listView1.Items[0] = new ListViewItem("Replaced item");

TreeView: New node is inserted.
ListView: Existing item is replaced.

I wonder why it works like that.

Okay, now I plead "flashback to VB6." It's the only explanation I have left.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top