Applying ++ and += to properties

  • Thread starter Thread starter Harlan Messinger
  • Start date Start date
H

Harlan Messinger

If I had

public int Count { get; set; }
int x, y;
...

Count = 0;
Count++;
x = Count;
Count += 1;
y = Count;

what would be the values of x and y? Are both operations treated as
though they were

Count = Count + 1;

or are the semantics different from that?
 
Sorry I was surprised as you seemed to know the response and it is very easy
to run this code by yourself. My understanding is now that you perhaps saw
how they perform by running the code but that you never saw them described
(??)

You'll find a description of those operators (and all others you could find)
at :
http://msdn.microsoft.com/en-us/library/6a71f45d.aspx

or are you bothered by something when they are applied specifically to a
property ?
 
Patrice said:
Hello,


And if you just try, do you see something unexpected ?

Since the idea came to mind while I was here, and since the idea of "why
or why not" went along with it, I thought I'd just ask.
 
Patrice said:
Sorry I was surprised as you seemed to know the response and it is very easy
to run this code by yourself. My understanding is now that you perhaps saw
how they perform by running the code but that you never saw them described
(??)

You'll find a description of those operators (and all others you could find)
at :
http://msdn.microsoft.com/en-us/library/6a71f45d.aspx

or are you bothered by something when they are applied specifically to a
property ?

I was following up on another thread I read here in which the semantic
differences between using a member value variable and using an automatic
value property in various contexts was being discussed, and I wondered
how that analysis extends to the semantics of the ++ and += operators.
 
Harlan said:
I was following up on another thread I read here in which the semantic
differences between using a member value variable and using an automatic
value property in various contexts was being discussed, and I wondered
how that analysis extends to the semantics of the ++ and += operators.

Note that as that previous discussion pertains to properties, it's to
all properties, not just automatic ones.

As far as the semantics of the ++ and += operators goes, hopefully the
C# programming guide on MSDN can help you understand them better. But
generally:

-- The += is the same as the + operator. In fact, it uses the +
operator. When you write x += y, that's the same as x = x + y, with the
caveat that if "x" is evaluated only once (which could be significant if
the "x" is actually a complex expression)

-- The ++ operator may be overloaded, but has the same semantics
regardless (i.e. the overloading may affect the exact modification that
happens, but not the semantics related to what the operator acts on)

Even with a property that's a value type, the use of the ++ and +=
operators should not be an issue. That's because the expression the
operator affects is still the property. In the case of the += operator,
that's a consequence of the fact that the expression is equivalent to
using the + operator followed by the = operator. In the case of the ++
operator, the C# specification (see 7.5.9 in the 3.0 spec) specifically
calls out properties (and indexers) to use the setter after the
operation to copy the result back to the original location.

This is different from the previous example, because methods are _not_
treated this way.

Methods don't have any particular "modify this" syntax, and so there's
not really a way to treat that kind of access of properties in a clear,
predictable-yet-efficient way. Because of their nature, a programmer
knows (or should know) that ++ and += always modify the operand, but
there are lots of examples where a method invocation wouldn't and/or
shouldn't modify the instance of the invocation, so that's not
implicitly part of a method invocation off something retrieved from a
property accessor.

Pete
 
Peter said:
Note that as that previous discussion pertains to properties, it's to
all properties, not just automatic ones.

As far as the semantics of the ++ and += operators goes, hopefully the
C# programming guide on MSDN can help you understand them better. But
generally:

-- The += is the same as the + operator. In fact, it uses the +
operator. When you write x += y, that's the same as x = x + y, with the
caveat that if "x" is evaluated only once (which could be significant if
the "x" is actually a complex expression)

-- The ++ operator may be overloaded, but has the same semantics
regardless (i.e. the overloading may affect the exact modification that
happens, but not the semantics related to what the operator acts on)

Even with a property that's a value type, the use of the ++ and +=
operators should not be an issue. That's because the expression the
operator affects is still the property. In the case of the += operator,
that's a consequence of the fact that the expression is equivalent to
using the + operator followed by the = operator. In the case of the ++
operator, the C# specification (see 7.5.9 in the 3.0 spec) specifically
calls out properties (and indexers) to use the setter after the
operation to copy the result back to the original location.

Thanks, this is what I just wanted to make sure of. I was picturing a
situation where you'd wind up incrementing a *copy* of the value
underlying the property, to no avail.
 
Harlan said:
Thanks, this is what I just wanted to make sure of. I was picturing a
situation where you'd wind up incrementing a *copy* of the value
underlying the property, to no avail.

I believe that this is not possible. I admit, it's been awhile since I
thought about it, and I may be overlooking something. But, when the
compiler knows the intended semantics, as is the case with the ++
operator, or when using "ref" or "out" arguments to a method, it will
detect the improper use of a value type with something with those
"modify this" semantics, and emit a compile-time error.

For example, consider this code:

struct Mutable
{
public int i;
}

class Holder
{
public Mutable Mutable { get; set; }
}

class Test
{
void Method()
{
Holder holder = new Holder();

holder.Mutable.i++;
}
}

The compiler will emit an error at the last statement, because
"holder.Mutable" is not a variable, and so it's not legal to modify the
field "i" in it (basically, the modification is known for sure to be not
visible after the statement, so it's not allowed).

Even so, as you've seen in the previous example (in the other thread),
there are still situations where the compiler can't know that the change
is not visible, or should otherwise be prohibited (e.g. a method that
has a useful side-effect even if there's a mutation that won't be seen),
and so you can wind up with a bug instead of a compiler error.

That's one of the biggest reasons, if not THE biggest reasons, my
opinion is that mutable value types should be avoided altogether. It's
very easy, even for experienced programmers, to overlook either the
mutable aspect or the value type aspect and introduce bugs, or at the
very least find it difficult to track down a bug someone else
introduced. Immutable data types and value types go hand in hand for a
lot of reasons, and they are less likely to lead to these bugs.

Pete
 
Peter said:
I believe that this is not possible. I admit, it's been awhile since I
thought about it, and I may be overlooking something. But, when the
compiler knows the intended semantics, as is the case with the ++
operator, or when using "ref" or "out" arguments to a method, it will
detect the improper use of a value type with something with those
"modify this" semantics, and emit a compile-time error.

For example, consider this code:

struct Mutable
{
public int i;
}

class Holder
{
public Mutable Mutable { get; set; }
}

class Test
{
void Method()
{
Holder holder = new Holder();

holder.Mutable.i++;
}
}

The compiler will emit an error at the last statement, because
"holder.Mutable" is not a variable, and so it's not legal to modify the
field "i" in it (basically, the modification is known for sure to be not
visible after the statement, so it's not allowed).

Even so, as you've seen in the previous example (in the other thread),
there are still situations where the compiler can't know that the change
is not visible, or should otherwise be prohibited (e.g. a method that
has a useful side-effect even if there's a mutation that won't be seen),
and so you can wind up with a bug instead of a compiler error.

That's one of the biggest reasons, if not THE biggest reasons, my
opinion is that mutable value types should be avoided altogether. It's
very easy, even for experienced programmers, to overlook either the
mutable aspect or the value type aspect and introduce bugs, or at the
very least find it difficult to track down a bug someone else
introduced. Immutable data types and value types go hand in hand for a
lot of reasons, and they are less likely to lead to these bugs.

My understanding of the term "mutable variable types" includes the likes of

int i;

Surely you don't mean those should be avoided?
 
Peter said:
For example, consider this code:

struct Mutable
{
public int i;
}

class Holder
{
public Mutable Mutable { get; set; }
}

class Test
{
void Method()
{
Holder holder = new Holder();

holder.Mutable.i++;
}
}

The compiler will emit an error at the last statement, because
"holder.Mutable" is not a variable, and so it's not legal to modify the
field "i" in it (basically, the modification is known for sure to be not
visible after the statement, so it's not allowed).

IIRC, C# version 1 did not have this check, so it would happily get a
copy of the value and modify it. Of course causing some confusion when
people used mutable structs like Point, and they suddenly wouldn't
change when they made a property of it...
 
Harlan said:
My understanding of the term "mutable variable types" includes the likes of

int i;

Variables are, well...variable. By definition, they are mutable.
However, Int32 is _not_ mutable. It's immutable. When you write:

i = 5;
i++;

You are not modifying the instance of Int32 that has the value of 5 and
changing it to 6. You are replacing the instance of 5 stored in the
variable "i" with the instance of 6, now stored in the variable "i".

Pete
 
Peter said:
Variables are, well...variable. By definition, they are mutable.
However, Int32 is _not_ mutable. It's immutable. When you write:

i = 5;
i++;

You are not modifying the instance of Int32 that has the value of 5 and
changing it to 6. You are replacing the instance of 5 stored in the
variable "i" with the instance of 6, now stored in the variable "i".

Right, but I wasn't asking about that, I was asking about simple, basic
ints. You wrote, "my opinion is that mutable value types should be
avoided altogether". The type *int* is a mutable value type. Therefore,
it looked like you were saying that *int* should be avoided, which I'm
sure is not what you meant, so I thought you might want to double-check
your words.
 
Harlan said:
Right, but I wasn't asking about that, I was asking about simple, basic
ints. You wrote, "my opinion is that mutable value types should be
avoided altogether". The type *int* is a mutable value type. Therefore,
it looked like you were saying that *int* should be avoided, which I'm
sure is not what you meant, so I thought you might want to double-check
your words.

The types int and System.Int32 are the same.

The int type is by definition not mutable. You can not take a part of an
int value and change it, you have to create a new value and replace the
old value.

int i = 41;
i++; // this creates the value 42 and stores in the variable
 
Harlan said:
Right, but I wasn't asking about that, I was asking about simple, basic
ints. You wrote, "my opinion is that mutable value types should be
avoided altogether". The type *int* is a mutable value type. Therefore,
it looked like you were saying that *int* should be avoided, which I'm
sure is not what you meant, so I thought you might want to double-check
your words.

Double-checked, and they are correct. See Göran's reply. The type
"int" is not a mutable value type. It's an alias for System.Int32 and
System.Int32 is immutable (as are all of the other primitive value types
similar to "int").

Pete
 
Peter said:
Double-checked, and they are correct. See Göran's reply. The type
"int" is not a mutable value type. It's an alias for System.Int32 and
System.Int32 is immutable (as are all of the other primitive value types
similar to "int").

I see. It's come back to me now from back when I originally learned
about C#. I guess I just haven't found reason to think about it since
then. But in that case, what makes it a value type? It seems that it
doesn't hold a value, but a reference to a changeable place in memory
where a value has been placed.
 
Thanks, this is what I just wanted to make sure of. I was picturing a
situation where you'd wind up incrementing a *copy* of the value
underlying the property, to no avail.

It's possible if this is what your property returns. Strictly speaking this
is not the fault of those operators. It all depends on how the property is
implemented (for example if you implament the count property as returning a
rndom number you ahve no way to knwo what values the code you posted in your
first message will return).
 
Harlan said:
I see. It's come back to me now from back when I originally learned
about C#. I guess I just haven't found reason to think about it since
then. But in that case, what makes it a value type?

You mean other than the fact that is _is_ a value type? :)

The short answer is: because it's a "struct" and not a "class".

That's the mechanical explanation for why it's a value type. I'm not
sure what other answer you might be expecting. I mean, I could use the
behavior of System.Int32 to argue that's why it's a value type. But in
reality, it's the other way around. That is, it has that behavior
_because_ it's a value type. The only thing that "makes it a value
type" is the fact that that's how it was implemented in the language and
framework.
It seems that it
doesn't hold a value, but a reference to a changeable place in memory
where a value has been placed.

Unfortunately, I think you don't really have a full understanding of the
concepts of a variable, versus a value type or reference type. It's
hard in this context to provide a good explanation – the forum isn't
really interactive enough for a proper discussion – but I'll try my best.

As I mentioned before, a variable is always mutable (in C#…in other
languages this is not necessarily true). And yes, the _variable_ can be
thought of as "a changeable place in memory where a value has been placed".

It's unfortunate that the words "value" and "reference" have slightly
different meanings depending on the context. But, they do. So
hopefully by pointing that out, I can use those words in their different
ways without making the explanation too confusing. With that said, here
goes nothin'… :)

The question of "value type" versus "reference type" has nothing to do
with the variable per se, but rather what that "value" that's in the
variable is. Note that all variables have a "value", even variables
that hold a "reference type". This is what I mean by the use of the
words in multiple ways.

Now, a variable holding a value type has as its value the value type
itself. The storage allocated for the variable _is_ the value type
instance itself. You can modify the contents of that storage by
assigning a new instance of a value type to the variable. Or, if the
value type is mutable, you can do something that will ultimately modify
a field in the value type (assign to the field, or call a method or
property that assigns to the field).

But in this case, the storage the variable represents is modified _only_
if you are using that variable representing the value type itself. Any
other way of getting the value type instance (e.g. returned from a
property) will create a copy of the original value, and the operation
will modify the copy, not the value in the original storage location.

A variable holding a reference type has as its value the _reference_ to
the instance of that type, which is actually stored elsewhere. The
storage allocated for the variable is only that reference, while the
storage for the reference type instance is elsewhere (i.e. the heap).

For a reference type variable, if you modify the contents of the
variable, you are simply copying a different reference into the
variable. Nothing happens to the object that variable was previously
referencing. Conversely, if you modify the object the variable was
referencing, you will see that modification immediately when accessing
the instance via that variable _and_ via any other variable that was
referencing that same instance, while nothing at all happens to the
variable's value itself.

So, when we look at the type System.Int32 (i.e. "int"), because it's
declared as a "struct" and not a "class", it is by declaration and by
definition a value type. This gives it the specific value type behavior
described above.

Furthermore, there's nothing in the System.Int32 struct that allows you
to modify an instance of System.Int32. The only thing you can ever do
is assign a new value of System.Int32 to a variable of that type. This
does mutate the _variable_, but not the original instance of
System.Int32 the variable used to hold. A whole new instance of
System.Int32 is copied into the variable.

Thus, System.Int32 is a value type (because it's declared as "struct")
and it's immutable (because there's nothing in the type that allows you
to modify an instance of the type…you can only copy a new instance over
an old one).


I will now digress a bit… :)

I think part of the problem is that because many programmers are so used
to using primitive types without ever thinking about mutability or
immutability, their internal conceptualization of the behavior of the
types is too imprecise. I know that happened to me when I starting
using C# after years of C++.

The same concepts of mutability can apply in other languages, such as
C++. But C++ doesn't provide the same kind of clear division between
value types and reference types. Instead, any given structure or class
can act as either, depending on usage.

That is, technically in C++ _everything_ is basically a value type (i.e.
you can always store the instance in a variable and copy the whole
instance from one place to another), but because the language includes
an idiom for accessing class members via a pointer, you can effectively
treat a data structure as a reference type by passing that pointer
around instead of copies of the structure itself.

Note that in C++, because everything's a value type, you still have the
same issues with regards to mutability and value types. Consider this code:

class Mutable
{
private:
int _i;

public:
Mutable() : _i(0) { }
int getI() { return _i; }
void setI(int i) { _i = i; }
}

class Holder
{
private:
Mutable _m;

public:
Mutable getM() { return _m; }
void setM(Mutable m) { _m = m; }
}

void Method()
{
Holder holder;

holder.getM().setI(5);
printf("%d", holder.getM().getI());
}

What do you expect the above to print? "5"? Or "0"? Note that nowhere
in the code am I using pointers, and thus everything is being treated as
a value type (accessed via copy or variable).

We could instead make the Mutable class immutable (and of course change
the name to match). Then you would be forced to make changes in a way
that have more obvious semantics:

class Immutable
{
private:
int _i;

public:
Immutable(int i) { _i = i; }
int getI() { return _i; }
}

class Holder
{
private:
Immutable _m;

public:
Holder() : _m(0) { }
Immutable getM() { return _m; }
void setM(Immutable m) { _m = m; }
}

void Method()
{
Holder holder;

// This no longer is possible:
// holder.getM().setI(5);

// This has no effect, for obvious reasons:
holder.getM() = Immutable(5);

// Instead, you have to do this:
holder.setM(Immutable(5));

// And so the result of this statement is
// much easier to predict (because it's more
// consistent with the likely expectation
// of the programmer when doing operations
// that look like they are changing things):
printf("%d", holder.getM().getI());
}

And finally, note in the above that in C++, the type "int" is also
immutable. It's probably immutable in an even less interesting way than
in C#, because the type doesn't even have any members (at least in C#,
there's the _possibility_ of a method or property in System.Int32 that
could modify an instance of the type, even though of course none
actually exists). But just like in C#, you cannot modify a given
instance of "int"; you can only modify a _variable_ holding an instance
of "int", by copying a whole new instance of "int" into the variable.

Pete
 
Harlan said:
I see. It's come back to me now from back when I originally learned
about C#. I guess I just haven't found reason to think about it since
then. But in that case, what makes it a value type? It seems that it
doesn't hold a value, but a reference to a changeable place in memory
where a value has been placed.

Well, there is a reference to the changeable memory area, but it's the
memory area that is the variable, not the reference. You can change
what's stored in the variable, but you can't change the reference to it.

Also, the variable doesn't have the reference all by itself. The
reference is either a reference to the class containing the variable, or
a pointer to a stack frame containing the variable. So, the reference to
the variable itself doesn't exist all the time, it's calculated by
adding an offset to another reference when you need to access the variable.
 
Back
Top