2013-10-02 70 views
2

大多数消息来源表示,重载++和 - 运算符在c#中的结果会同时重载两者,postfix和prefix。但看起来他们的行为仍然不同。C#后缀和前缀增量/减量过载差异

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

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

+0

您的'operator ++'中存在一个错误:您不仅为递增的值返回一个对象,而且修改该操作符所应用的对象。它应该是:'公共静态计数器运算符++(计数器c){返回新的计数器(c.v + 1); }' – dtb

+0

但是这是增量应该做的事情,'我++'必须改变'我'的价值,不是吗? – Nick

+0

编号'i ++'记得变量'i'的值(我们称之为'i_old'),计算值'i + 1'(我们称之为'i_new'),将值'i_new'存入变量'i',并返回值'i_old'。 – dtb

回答

9

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

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

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

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

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

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

有了这个正确的执行你的程序是这样的:

Counter c1 = new Counter(1); 

称之为C1指现在W的对象。 W.v为1

Counter c2 = c1++; 

这具有的语义:

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

所以c1现指X,并c2WW.v是1和X.v是2

Counter c3 = ++c1; 

这具有

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

所以C1的语义和现在c3分别都是指对象Y,和Y.v是3。

c3++; 

这有

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

语义所以当烟雾清除所有:

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

X是孤立的。

这应该给出完全相同的结果,如果您将c1,c2c3视为正常整数。

+0

在某些方面,'operator' *声明*使用'++'和'--'作为“加一”和“减一”操作的名称,这太糟糕了;我想知道,如果有一个否则无效的标记序列,它会或多或少地混淆[例如, '+%']表示运算符重载计算的值,所以'X = Y +%'会将'X'设置为由'++ Y'计算的值,但不回写'Y '。如果存在这样一个运算符,那么'+%'超载和前缀/后缀运算符之间的关系会比使用'++'重载更直观。 – supercat

+0

@supercat:如果存在这样的操作符,则用户不太直观,必须使用它来重载++和 - 。但我同意你的意见,但是,当你重载++和 - 时,你不应该直观地指出你不应该改变对象。无论哪种方式,那里都有一些非直觉性。 – RobH

+0

@RobH:我不明白这是多么的不直观,除了特殊的编译器生成的“过载”事件,'+ ='和' - ='操作符被重载重载'+ ''和'-',我不是特别喜欢btw的设计,特别是像'Delegate'这样的类型,它们的行为应该像值。如果'evt'是一个自动事件,那么'evt + = someDelegate'语句会将'someDelegate'自动添加到'evt';它*不等同于'evt = evt + someDelegate;'。不幸的是,没有办法让操作员超载,让其他代表这样做。 – supercat