Post-increment Operator Overloading
我在试图用C_重载后增量运算符时遇到问题。使用整数,我们得到以下结果。
1 2 3 4 5 6 7 8 9 10 11 | int n; n = 10; Console.WriteLine(n); // 10 Console.WriteLine(n++); // 10 Console.WriteLine(n); // 11 n = 10; Console.WriteLine(n); // 10 Console.WriteLine(++n); // 11 Console.WriteLine(n); // 11 |
但是,当我使用类尝试它时,看起来对象是交换的。
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 | class Account { public int Balance { get; set; } public string Name { get; set; } public Account(string name, int balance) { Balance = balance; Name = name; } public override string ToString() { return Name +"" + Balance.ToString(); } public static Account operator ++(Account a) { Account b = new Account("operator ++", a.Balance); a.Balance += 1; return b; } public static void Main() { Account a = new Account("original", 10); Console.WriteLine(a); //"original 10" Account b = a++; Console.WriteLine(b); //"original 11", expected"operator ++ 10" Console.WriteLine(a); //"operator ++ 10", expected"original 11" } } |
调试应用程序时,重载的operator方法会返回旧值(10)的新对象和通过引用传递的对象,并具有新值(11),但最终会交换对象。为什么会这样?
我的第一个想法是指出++的正常语义是就地修改。如果你想模仿你会写的:
1 2 3 4 5 | public static Account operator ++(Account a) { a.Balance += 1; return a; } |
不创建新对象。
但后来我意识到你在模仿后增量。
所以我的第二个想法是"不要这样做"——语义并没有很好地映射到对象上,因为被"使用"的值实际上是一个可变的存储位置。但是没有人喜欢被一个随机的陌生人告诉"不要这样做",所以我会让微软告诉你不要这样做。我担心他们的话在这些问题上是最终的决定。
另外,关于它为什么要做它所做的,您实际上覆盖了preincrement操作符,然后像使用post increment操作符一样使用它。
关键在于理解
1 2 | Account b = a; a++; |
这就是它将要执行的命令。分配有效地发生在增量之前(1)。所以,这一行的第一个效果是A和B都引用了原始对象A。
现在将计算++部分。在operator方法内部,我们增加原始对象的
但是,您已经在operator方法中创建了一个新对象,并将其作为运算符的输出返回。现在将更新a以指向新创建的对象。
现在a指向一个新对象,而b继续指向原始对象。这就是为什么写行输出出现交换的原因。
正如@markusq所指出的那样,++操作符是用来进行就地修改的。通过生成一个新对象,你就打破了这个假设。对象上的运算符重载是一个棘手的问题,这是一个很好的例子,说明为什么在大多数情况下最好避免它。
1-为了精确起见,在处理对象上的运算符时,赋值实际上不会发生在增量之前,但在这种情况下,最终结果是相同的。实际上,复制原始对象引用,对原始对象执行操作,然后将复制的引用分配给左侧变量。如果你假装作业先发生,解释起来就简单多了。
真正发生的是:
1 | Account b = a++; |
结果是,由于++运算符如何处理对象:
1 2 3 4 5 6 7 | Account copy = a; Account x = new Account("operator ++", a.Balance); a.Balance += 1; // original object's Balance is incremented a = x; // a now points to the new object, copy still points to the original Account b = copy; // b and copy now point at the same, original, object |
您应该始终返回更改的值。C将此值用作新值,并根据操作员的需要返回旧值或新值。