Why does this.Location.X = 0; generate a compile error?

  • Thread starter Thread starter Dave
  • Start date Start date
D

Dave

..X (.Y too) are ints with get/set methods so why does trying to set either
one generate a compile error??
 
This is something that gets many developers confused. As you probably
already know, "Location" is not a variable but a property. This means that
the property internal (get) implementation looks something like this:

public Point Location
{
get { return _Point; }
}

This code is then translated by the compiler to a method like:

public Point get_Location()
{
return _Point;
}

And there is the problem, what you are getting out of this function is a
fresh new Point variable, not a direct reference to the _Point varaible.

Keyword here is you are getting a full *new* Point variable so what you are
basicaly trying to do is telling the compiler to put a "Point" sturct value
into a "Point.X" wich is not a variable and that's why you get the error.

What you need to do is to first create a new Point varaible with the new
values and then use that to set the new value as:

Point myNewPoint == new Point();
myNewPoint.X = 123;
myNewPoint.Y = 456;
this.Location = myNewPoint;
 
Ah, because Point is a struct and not a class and structs are a value type.
I get it.

Thanks.
 
Not really, you can actually do what are doing as long as you had a direct
reference to the Point variable:
-----------------------------------
public class Test
{
public Point _Point = new Point();
}

Test myTest = new Test();
myTest._Point.X = 0; // This will work
 
Rene said:
Not really, you can actually do what are doing as long as you had a
direct reference to the Point variable:

.... or if Point was a reference type, in which case the property getter
doesn't return a copy of the variable, but rather a new reference to it.

-cd
 
Rene said:
Not really, you can actually do what are doing as long as you had a direct
reference to the Point variable:
-----------------------------------
public class Test
{
public Point _Point = new Point();
}

Test myTest = new Test();
myTest._Point.X = 0; // This will work
-----------------------------------

This is not about value type, this is about the fact that internally you are
getting and setting a *Full* Point struct, so assigning just a member of a
struct does not work.

No, it *is* about value types. It's specifically described in the C#
spec, section 14.13.1 (ECMA numbering, 1.1 edition)

<quote>
When a property or indexer declared in a struct-type is the target of
an assignment, the instance expression associated with the property or
indexer access must be classified as a variable.
</quote>

So it's because it's a property *and* because it's accessing a value
type. If this rule did not exist, you could do this.Location.X=0, but
it would be equivalent to:

Point p = this.Location;
p.X=0;

which would be a no-op. The compiler is guarding you against that, and
it's only an issue because Point is a value type.
 
Carl Daniel [VC++ MVP]
... or if Point was a reference type, in which case the property getter
doesn't return a copy of the variable, but rather a new reference to it.

No, it would return a copy of the variable's value. The variable isn't
the same as its value - what would be returned would have nothing to do
with the variable itself, it would just have the same value.
Specifically, there is nothing you could do with the returned value
which would change the value of the original variable. You could change
the contents of the object that value referred to, but that's not
changing the variable's value.
 
Jon Skeet said:
Carl Daniel [VC++ MVP]
... or if Point was a reference type, in which case the property getter
doesn't return a copy of the variable, but rather a new reference to it.

No, it would return a copy of the variable's value. The variable isn't
the same as its value - what would be returned would have nothing to do
with the variable itself, it would just have the same value.
Specifically, there is nothing you could do with the returned value
which would change the value of the original variable. You could change
the contents of the object that value referred to, but that's not
changing the variable's value.

I think that we all know what he means.

The lesson to be learned from this is that value types with setable
properties are a bad idea i.e. changing value types should be an all or
nothing affair.

Note there are at least as many problems caused by using reference types.
The usual scenario is that you have a class with something like a
LocationChangedEvent which is only raised on "obj.Location = newLocation;"
and not "obj.Location.X = 42;"

The real solution is C++ const but it isn't going to happen :(
 
Seeshhh John, you are such a nerd! Just when I was all happy about helping
out BAMM WHAMM here comes John with his closing remarks! :)

The point that I wanted to get across was that properties look like fields
but they are really methods. I also wanted to point out the what the
original poster was doing it's possible to do as long as the "Location" was
a field.

Cheers
 
Nick Hounsome said:
I think that we all know what he means.

I suspect the post I was replying to does, but I've learned to my cost
that careless misuse of terminology can confuse those reading the
replies.
The lesson to be learned from this is that value types with setable
properties are a bad idea i.e. changing value types should be an all or
nothing affair.
Agreed.

Note there are at least as many problems caused by using reference types.
The usual scenario is that you have a class with something like a
LocationChangedEvent which is only raised on "obj.Location = newLocation;"
and not "obj.Location.X = 42;"

Hmm... I'd say at that point that it's carelessness on the part of the
class designer - either the Location (or whatever) class, or the parent
class. If the Location class is designed to cope with situations like
this, the parent could subscribe to *its* change event. I've rarely
found this kind of thing particularly necessary though.

I find that reference semantics are generally a lot easier to
understand - there are fewer "gotchas", partly due to the lack of
boxing/unboxing.
The real solution is C++ const but it isn't going to happen :(

Indeed.
 
If this rule did not exist, you could do this.Location.X=0, but
it would be equivalent to:

Point p = this.Location;
p.X=0;


I am not sure if I agree with that because if I follow the same logic, if I
try to set the "this.Top = 0" it would be equivalent to:

int i = this.Top;
i = 0;

This would also be a no-op (I assume this is stands for no-operation) so the
compiler should not allow me to do that either but it does.

Going back to the Location property again, In my opinion, the compiler will
internally try to use the set_Location(Point newPoint) function to set the
new value for the Location property. As we all already know, this function
is automatically created by the compiler.

The only thing that makes sense to pass as the argument to this function is
a full Point variable and not just a member like "someNewPoint.X".

You just can't partially change a Point value like that; it would be like
trying to change an integer value of zero to a one by trying to pass only
the one bit that it needs to change form a 0 to a 1. You don't do that,
what you do is to pass the whole integer value as in "00000000 00000000
00000000 00000001".

I believe this is really why the compiler won't allow the code to compile.
 
Rene said:
I am not sure if I agree with that because if I follow the same logic, if I
try to set the "this.Top = 0" it would be equivalent to:

int i = this.Top;
i = 0;

No, it wouldn't - because this.Top is a property on its own, and that's
okay because "this" is a variable. You can set a property on an value
type expression that is classified as a variable, but not on a value
type expression that is classified as a value (as per the spec I quoted
before).
This would also be a no-op (I assume this is stands for no-operation) so the
compiler should not allow me to do that either but it does.

Going back to the Location property again, In my opinion, the compiler will
internally try to use the set_Location(Point newPoint) function to set the
new value for the Location property. As we all already know, this function
is automatically created by the compiler.

No, it won't use the Location setter, because you can't do it in the
first place. It *shouldn't* use the Location setter either, because you
haven't said to. If the Location setter had some other side effect, it
would be highly counter-intuitive to have that side effect come when
nothing had done

this.Location = said:
The only thing that makes sense to pass as the argument to this function is
a full Point variable and not just a member like "someNewPoint.X".

Yes, you can only give a full value to the Location setter. But the
Location setter isn't being called here. The only setter being called
is the one on Point. The Location property *getter* is called.
You just can't partially change a Point value like that; it would be like
trying to change an integer value of zero to a one by trying to pass only
the one bit that it needs to change form a 0 to a 1. You don't do that,
what you do is to pass the whole integer value as in "00000000 00000000
00000000 00000001".

No, you *can* change half a Point value exactly like that, because
(unfortunately IMO) Point is a mutable value type. However, changing
the value of any part of one Point variable won't change the value of
any other Point variable.
I believe this is really why the compiler won't allow the code to compile.

Not really - it's because invoking the accessor (the "get" method)
returns a value to the caller. Modifying that value (whether half of it
or not) won't do anything. The point is that it's *only* using the
getter, *not* the setter.

I suggest you have a look at the thread called "csharp language idea"
started on April 27th in the C# group for more about this.
 
Back
Top