C#postfix和前缀增量/减量重载差异

C# postfix and prefix increment/decrement overloading difference

大多数资料表明,重载C中的+和-运算符会导致同时重载后缀和前缀。但看起来他们的行为还是不同的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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());
    }
}

奇怪的是,虽然重载的operator ++返回原始类的副本,但在本例中,c1c3成为对同一对象的引用,而c2指向不同的对象(此处为c1=4, c2=2, c3=4)。将Counter c3 = ++c1;改为Counter c3 = c1++;输出c1=3, c2=2, c3=4

那么,后缀和前缀增量/减量之间的确切区别是什么,以及它如何影响重载?这些运算符对类和基元类型的作用是否相同?


这是用C实现递增和递减的错误方法。如果你做错了,你会得到疯狂的结果;你做错了,你得到了疯狂的结果,所以系统工作。-)

巧合的是,上周我写了一篇关于这个主题的文章:

http://ericlippert.com/2013/09/25/bug-guys-meets-math-from-scratch/从头开始/

正如评论者DTB所指出的,正确的实现是:

1
2
3
4
    public static Counter operator ++(Counter c)
    {
        return new Counter(c.v + 1);
    }

在C中,递增运算符不能改变其参数。相反,它必须只计算递增的值并返回它,而不会产生任何副作用。改变变量的副作用将由编译器处理。

有了这个正确的实现,您的程序现在可以这样进行:

1
    Counter c1 = new Counter(1);

调用c1现在引用的对象WW.v为1。

1
    Counter c2 = c1++;

其语义如下:

1
2
3
temp = c1
c1 = operator++(c1) // create object X, set X.v to 2
c2 = temp

因此,c1现在指Xc2WW.v为1,X.v为2。

1
    Counter c3 = ++c1;

它的语义是

1
2
3
temp = operator++(c1) // Create object Y, set Y.v to 3
c1 = temp
c3 = temp

所以c1和c3现在都指的是对象YY.v是3。

1
    c3++;

它的语义是

1
c3 = operator++(c3) // Create object Z, set Z.v to 4

所以当烟雾全部散去时:

1
2
3
c1.v = 3 (Y)
c2.v = 1 (W)
c3.v = 4 (Z)

以东十一〔三〕是孤儿。

这将产生与使用c1c2c3作为普通整数完全相同的结果。