8

There is probably a quite logical explanation to this, but I have a question.

Let's say I have a variable of type Rectangle called _rect. I can now say _rect.X = 50; without any problems.

Now I have a class with a property called Rect that exposes the internal variable _rect.

Then, if I try to write Rect.X = 50; I get the following compilation error:

Cannot modify the return value of 'TestClass.Rect' because it is not a variable.

I can write Rect = new Rectangle( 50, Rect.Y, Rect.Width, Rect.Height) like for a immutable type, but for non-immutable types, are there any other way of doing this?

I want to use auto-properties for this rectangle field, but it's really annoying not being able to modify it inside the class itself.

Are there any way short of making a backing field and dropping the auto-property?

Øyvind Bråthen
  • 59,338
  • 27
  • 124
  • 151

4 Answers4

10

The reason for this error is because Rectangle is a value type (struct) in contrast to reference types (classes). You cannot modify the X property because when you use the Rect property getter a new value for the rectangle is returned (the getter is a function). If it was a reference type you are manipulating the pointer and this would be possible.

This is an important aspect of value vs reference types to be aware of.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • To be sure, there’s no *technical* reason why the compiler couldn’t make this work. The compiler generates all kinds of extra code for the user (e.g. boxing), so why not here? (In fact, the reason is probably that it isn’t deemed important since structs should be immutable anyway.) – Konrad Rudolph Sep 20 '10 at 07:33
  • @Konrad - structs are copied when passed as a result, so the operation simply wouldn't make any sense. It's not because of immutability (structs are mutable). But if you retrieve a copy of an object and change the field in that copy - that's simply a NOOP. – viraptor Sep 20 '10 at 08:33
  • @viraptor: I disagree. The operation obviously makes sense: mutate a property. The compiler could easily make this work by generating code equivalent to `var tmp = foo.Property; tmp.x = newX; foo.Property = tmp;` – The compiler actually **does** this for an equivalent situation: When you have a value type property and write `foo.Property += 1` this **works** (tested on Mono compiler). The compiler clearly rewrites the code to the above. – Konrad Rudolph Sep 20 '10 at 10:38
  • @Konrad: These cases are not equivalent - if the type of `Property` is a struct, then for `foo.Property.x += 1` you get the equivalent of `tmp=foo.Property; tmp.x+=1` (where `tmp` becomes a *copy* of the property). If the property itself is a simple value type, then you don't modify anything about the value itself. You generate a new value and assign to the property itself, not a part of it. Property getter guarantees that you get the *copy* of the type. You get a *copy* of `1` and assign back `2` for example. If you get a *copy* of `foo.x` and modify it, you assign to the *copy* of `x`. – viraptor Sep 20 '10 at 10:52
  • @viraptor: I agree, I’ve just noticed that difference myself – see also comments below http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil/441323#441323 – Konrad Rudolph Sep 20 '10 at 10:55
1

Accessing the property is really a function call which returns a copy of the value type. Rect.X = 50; would only modify this temporary copy, not the backing field itself.

When the property is not auto-implemented, you could create an additional property RectX, which can be used to get and set the X property of the rectangle.

Henrik
  • 23,186
  • 6
  • 42
  • 92
  • I feel realyl stupid now, for not understanding why new Rectangle(...) worked, and not Rect.X = 50, since both would have worked on the copy, but when I write new Rectangle I'm calling the set instead of get. Thanks for making my own head clear regarding this. So that basically means no then. If I want that type of behaviour, I have to use a backing field. – Øyvind Bråthen Sep 20 '10 at 07:26
  • When using a backing field the behaviour is still going to be the same, the getter and setter continue to be methods, so still you will be getting a value copy of the Rectangle instance. The behaviour you want is only for Reference types, because then you get the "reference" to the original object instance. However rectangle is a Value type. – Iñaki Elcoro Sep 20 '10 at 07:39
0

Check this: http://www.albahari.com/valuevsreftypes.aspx

Sidharth Panwar
  • 4,606
  • 6
  • 27
  • 36
0
  class RectangleWithoutFields 
  {
     // just autoproperties, no fields

     public int X { get; set; }

     public int Y { get; set;}


     public RectangleWithoutFields()
     {
         X = 0;

         Y = 0; 
     }

     public void ChangeProperties(int x, int y)

     {
         X = x;

         Y = y;
     }
 }
nawfal
  • 70,104
  • 56
  • 326
  • 368
explorer
  • 11,710
  • 5
  • 32
  • 39