Most sources says that overloading ++ and -- operators in c# results overloading both, postfix and prefix at once. But it looks like their behaviour is still different.
class Counter
{
public Counter(int v = 0)
{
this.v = v;
}
public Counter(Counter c)
{
v = c.v;
}
public int GetValue() { return v; }
public static Counter operator ++(Counter c)
{
c.v++;
return new Counter(c);
}
private int v;
}
class Program
{
public static void Main()
{
Counter c1 = new Counter(1);
Counter c2 = c1++;
Counter c3 = ++c1;
c3++;
System.Console.WriteLine("c1 = {0}", c1.GetValue());
System.Console.WriteLine("c2 = {0}", c2.GetValue());
System.Console.WriteLine("c3 = {0}", c3.GetValue());
}
}
Wonderfully, although overloaded operator ++
returns copy of the original class, in this example c1
and c3
becomes references to the same object, while c2
points to different one (c1=4, c2=2, c3=4
here). Changing Counter c3 = ++c1;
to Counter c3 = c1++;
outputs c1=3, c2=2, c3=4
.
So, what is the exact difference between postfix and prefix increment/decrement and how it affects overloading? Are these operators acts same way for classes and for primitive types?
This is the wrong way to implement increment and decrement in C#. You will get crazy results if you do it wrong; you did it wrong, you got crazy results, so the system works. :-)
Coincidentally I wrote an article about this very subject last week:
http://ericlippert.com/2013/09/25/bug-guys-meets-math-from-scratch/
As commenter dtb points out, the correct implementation is:
In C# the increment operator must not mutate its argument. Rather it must only compute the incremented value and return it, without producing any side effects. The side effect of mutating the variable will be handled by the compiler.
With this correct implementation your program now goes like this:
Call the object that c1 refers to right now
W
.W.v
is 1.This has the semantics of:
So
c1
now refers toX
, andc2
refers toW
.W.v
is 1 andX.v
is 2.This has the semantics of
So c1 and c3 now both refer to object
Y
, andY.v
is 3.This has the semantics of
So when the smoke all clears:
and
X
is orphaned.This should give exactly the same results as if you'd had
c1
,c2
andc3
as normal integers.