1

I am confused with structs.

class A
{
  double a { get; set; }  
  MyStruct b { get; set; }

  void Modify()
  {
     a = 1.0;    // This compiles.
     b.c = 2.0;  // Assuming c is a double. This gives the known error.
  }
}

Now why I am able to set the value of the structure a, and not of the structure's field b ? The answer I read is that I have a copy of b and not b's backing field. By the same logic I must have a copy of a, not a, so how it explains that the a property is changed after the permitted assignement

a = 1.0;

? Thank you.

EDIT :
Also in order to still use b inside the A class, "filtered" by the set and get methods and not directly by using the backing field _b, something which could look like this:

MyStruct _b;
MyStruct b { get { return _b; } set { _b=value; } }

and then use the field

_b

, as correctly stated in the answers, I thinked of a silly method to "wrap" the property in an one dimensional array of size 1.

MyStruct[] b { get; set; }

kind of wrapping the struct with a class. I dont know the computational cost of this, but I preferred it from using the field directly inside the A class.

ANSWERED : Ok, the confusion in this question comes, when you are used to interpret properties as fields in your mind. If you interpret properties the correct way -as the MsIL does-, all things settle down. All the answers and comments below, are relevant. (Sorry for the bad English.)

Dimi_Pel
  • 109
  • 3
  • 8

4 Answers4

5

You're doing two different things. Here's an example of doing the same thing in both places:

a = 1.0;
b = new MyStruct(2.0);

In both of these cases, the assignment operator is calling the "setter" of the property. You can think of it as:

this.SetA(1.0);
this.SetB(new MyStruct(2.0));

As a more complicated example, if you wrote:

a = a + 1.0;

that's equivalent to:

this.SetA(1.0 + this.GetA());
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Hey,hey what's going on here in StackOverflow? Are you an automaton? :) Thank you Jon. – Dimi_Pel Jan 19 '11 at 14:27
  • Yes, Jon I dont know if my question has the wrong logic, but since you can manipulate the b structure (and a of course), eg replacing it with a new structure (new MyStruct(2.0) or 1.0) why cant you manipulate any parts of it also, and consider it a copy which will be discarded ?? Sorry for my bad English anyway. Where my thinking is wrong ?? – Dimi_Pel Jan 19 '11 at 14:55
  • @Dimitris GR, [yes he is](http://meta.stackexchange.com/questions/9134/jon-skeet-facts) – finnw Jan 19 '11 at 15:00
  • @Dimitris GR: You can copy the value to a variable and *then* modify parts of it, if your struct is mutable (which is a bad idea to start with, btw). The compiler is trying to stop you from modifying something which is actually a copy but which don't *realise* is a copy. – Jon Skeet Jan 19 '11 at 15:11
  • @Jon Skeet, yes but if b is a copy, why a is not a copy ?? I ll augment the example with a class also. – Dimi_Pel Jan 19 '11 at 15:18
  • @Dimitris: If you fetch `a`, it *is* a copy. But `a = ...` is *assigning* to the property, whereas `b.c = ` is *fetching* a property (a copy) and then trying to assign a field. They're different expressions. – Jon Skeet Jan 19 '11 at 15:24
  • @Jon Skeet, Yeap! that seems to be the answer I wanted to hear. Thank you again Jon for your time. Seemed a little "idiotic" question after all, but it managed to bugged me for a day. – Dimi_Pel Jan 19 '11 at 15:46
  • b.c = 2.0; would translate to MyStruct temp = get_b(); temp.c = 2.0; clearly not what you want. – Julien Roncaglia Jan 19 '11 at 15:52
  • @Dimitris: I wonder if you would find Jon Skeet's [Parameter passing in C#](http://www.yoda.arachsys.com/csharp/parameters.html) article (and Lee Richardson's [illustrated discussion](http://rapidapplicationdevelopment.blogspot.com/2007/01/parameter-passing-in-c.html) of it) to be helpful for understanding these concepts. The issues involved are similar. – Brian Jan 19 '11 at 20:11
  • @Brian, I red the first article this morning. Auto-implemented properties were to blame for my confusion mostly. After reading the answers and translate to normal methods setA and getA, I finally understood it. Thanks. – Dimi_Pel Jan 19 '11 at 20:29
4

Suppose you have an assignment to a member access of the form:

expr.field = value;

The value of a struct instance is, by definition, its value. If you are attempting to mutate the field of a struct then you must be attempting to mutate the storage location that stores the value. That is, you must be attempting to mutate the variable. The field is a variable which is a part of another variable.

Therefore, if expr is a struct type then expr must be a variable.

In your example, expr is not a variable. It's a property, and the result of a property access is a value, not a variable.

That might not be clear. Let's try an analogy.

Imagine you have a piece of paper that represents a value of a struct. The paper has a box on it labelled "c", which contains a number.

When you say "b.c = 2.0;" what that does is goes and finds the storage associated with b, and makes a photocopy of the piece of paper it finds there. You then erase the number on the copy you've been handed and replace it with 2.0. Does that do what you want? Of course not. It doesn't change the value of the piece of paper stored in b at all! Value types are copied by value. Because this doesn't do what you want, it is illegal in C#.

Now suppose you have a piece of paper that represents the value of a class. The paper has a box in it labelled "y" which contains a number.

When you say "x.y = 2.0;" what it does is goes and finds the storage associated with x, and hands you one end of a ribbon. The other end of the ribbon is attached to the piece of paper that is the storage associated with x. You follow the ribbon, find the box labelled y at the other end, and replace the number there with 2.0.

Reference types are copied by reference; you don't get a copy of the value in the storage, you get something that lets you find the storage.

That's the difference between value types and reference types; value types are copied by value, reference types are copied by reference.

Is that now clear?

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thank you Eric for your detailed answer. I am aware of pointers, my lack of understanding seems to be more related with the first part of your answer about properties. I have used only classes thus far, so with the introduction of structs, things have blown up and uncovered the misunderstood concepts! – Dimi_Pel Jan 19 '11 at 16:49
  • @Dimitris: Remember, a property access is just a more pleasant syntax for a method call. Your code "b.c = 2.0;" is nothing more than a nice way to write "this.get_b().c = 2.0" The result of the property is the value returned by the getter method, not a reference to the storage location that the value was fetched from. – Eric Lippert Jan 19 '11 at 16:52
  • what is the analogy with "a=1.0;" then? "this.get_a()=1.0;" ? – Dimi_Pel Jan 19 '11 at 17:00
  • @Dimitris: No, that becomes "this.set_a(1.0)". Since "a" is a property directly on the left side of an assignment it must be the property *being set*. Since "b" is a property directly on the left side of a member access dot, it must be the property *being fetched*. – Eric Lippert Jan 19 '11 at 17:15
1

This error occurs when you try to modify the value.

When you write a = 1.0, you're replacing the value - you're assigning the entire value type to a new value. a = 1.0 is equivalent to b = new MyStruct().

This is yet another reason why mutable structs are evil.

Community
  • 1
  • 1
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • So the concept is that I am allowed to replace the structure altogether but not parts of it ? – Dimi_Pel Jan 19 '11 at 14:37
  • @Dimitris GR: The problem is that structures are a value type. Grabbing an element of an array by its index returns that element by value. In the case of a reference, it is still returning the element by value (i.e., it is returning a reference by value). – Brian Jan 19 '11 at 20:08
1

Your logic is a little off.

In your first example, you're replacing a double with a brand new double.

In the second example, you're not replacing the struct with another struct. Instead you're trying to modify some field of the struct.

To do similar things, you would have to do:

a = 10.1;
b = new MyStruct(10.1);

a = 11.1;
b = new MyStruct(11.1);
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • Yes I know I do dissimilar things. I wanted to emphasize the concept of the pass-by-reference and by value. The question was that if I am able to manipulate the a property, why cant I do the same with the b property also (and all of its contents in addition). Thank you for your answer Justin. – Dimi_Pel Jan 19 '11 at 14:34