Home All Groups Group Topic Archive Search About

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

Author
27 Apr 2006 12:11 AM
Dave
..X (.Y too) are ints with get/set methods so why does trying to set either
one generate a compile error??

Author
27 Apr 2006 4:34 AM
Rene
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;

Show quote
"Dave" <n***@nowhere.com> wrote in message
news:OX0ML8YaGHA.4160@TK2MSFTNGP04.phx.gbl...
> .X (.Y too) are ints with get/set methods so why does trying to set either
> one generate a compile error??
>
Author
27 Apr 2006 4:55 AM
Dave
Ah, because Point is a struct and not a class and structs are a value type.
I get it.

Thanks.


Show quote
"Rene" <a@b.c> wrote in message
news:%235LZgObaGHA.4236@TK2MSFTNGP05.phx.gbl...
> 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;
>
> "Dave" <n***@nowhere.com> wrote in message
> news:OX0ML8YaGHA.4160@TK2MSFTNGP04.phx.gbl...
>> .X (.Y too) are ints with get/set methods so why does trying to set
>> either one generate a compile error??
>>
>
>
Author
28 Apr 2006 3:30 AM
Rene
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.


Show quote
"Dave" <n***@nowhere.com> wrote in message
news:eRxd%23abaGHA.1020@TK2MSFTNGP02.phx.gbl...
> Ah, because Point is a struct and not a class and structs are a value
> type. I get it.
>
> Thanks.
>
>
> "Rene" <a@b.c> wrote in message
> news:%235LZgObaGHA.4236@TK2MSFTNGP05.phx.gbl...
>> 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;
>>
>> "Dave" <n***@nowhere.com> wrote in message
>> news:OX0ML8YaGHA.4160@TK2MSFTNGP04.phx.gbl...
>>> .X (.Y too) are ints with get/set methods so why does trying to set
>>> either one generate a compile error??
>>>
>>
>>
>
>
Author
28 Apr 2006 4:00 AM
Carl Daniel [VC++ MVP]
Rene wrote:
> Dave wrote:
>> Ah, because Point is a struct and not a class and structs are a value
>> type. I get it.
>
> 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
Author
28 Apr 2006 6:23 AM
Jon Skeet [C# MVP]
Carl Daniel [VC++ MVP]
<cpdaniel_remove_this_and_nospam@mvps.org.nospam> wrote:
> Rene wrote:
> > Dave wrote:
> >> Ah, because Point is a struct and not a class and structs are a value
> >> type. I get it.
> >
> > 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.

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 - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
28 Apr 2006 7:28 AM
Nick Hounsome
Show quote
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MPG.1ebbc546cda7de1c98d173@msnews.microsoft.com...
> Carl Daniel [VC++ MVP]
> <cpdaniel_remove_this_and_nospam@mvps.org.nospam> wrote:
>> Rene wrote:
>> > Dave wrote:
>> >> Ah, because Point is a struct and not a class and structs are a value
>> >> type. I get it.
>> >
>> > 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.
>
> 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 :(
Author
28 Apr 2006 7:32 PM
Jon Skeet [C# MVP]
Nick Hounsome <N***@NickHounsome.Me.Uk> wrote:
> > 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.

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.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
28 Apr 2006 6:21 AM
Jon Skeet [C# MVP]
Rene <a@b.c> wrote:
Show quote
> 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.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
28 Apr 2006 6:49 PM
Rene
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


Show quote
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MPG.1ebbc4e523b4e2f98d172@msnews.microsoft.com...
> Rene <a@b.c> wrote:
>> 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.
>
> --
> Jon Skeet - <sk***@pobox.com>
> http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
> If replying to the group, please do not mail me too
Author
1 May 2006 9:06 AM
Rene
> 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.
Author
1 May 2006 4:49 PM
Jon Skeet [C# MVP]
Rene <a@b.c> wrote:
> > 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;

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 = <something>;

> 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.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

AddThis Social Bookmark Button