我在尝试在C#中重载后增量运算符时遇到问题。使用整数我们得到以下结果。
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
但是,当我尝试使用类时,它看起来就像交换了对象。
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),但最后交换对象。为什么会这样?
答案 0 :(得分:14)
我的第一个想法是指出++的正常语义是就地修改。如果你想模仿你写的:
public static Account operator ++(Account a)
{
a.Balance += 1;
return a;
}
而不是创建新对象。
但后来我意识到你试图模仿后期增量。
所以我的第二个想法是“不要那样做” - 语义根本不能很好地映射到对象上,因为“使用”的值实际上是一个可变的存储位置。但是没有人喜欢被一个随机的陌生人告知“不要那样做”所以我会让Microsoft tell you not to do it。我担心他们的话在这些问题上是最终的。
P.S。至于为什么它正在做它做的事情,你真的重写了preincrement运算符,然后使用它就好像它是postincrement运算符。
答案 1 :(得分:12)
关键是要了解行Account b = a++;
的工作原理。鉴于您的代码是如何编写的,这一行等同于:
Account b = a;
a++;
这就是它将执行的顺序。有效的赋值(1)在增量之前发生。因此,这一行的第一个作用是 a 和 b 都引用原始对象 a 。
现在将评估++部分。在operator方法内部,我们递增原始对象的Balance
。此时 a 和 b 都指向原始版本,Balance
为11, b 将继续执行如此。
但是,您已在operator方法中创建了一个新对象,并将其作为运算符的输出返回。 a 现在将更新为指向新创建的对象。
因此, a 现在指向一个新对象,而 b 继续指向原始对象。这就是WriteLine输出出现交换的原因。
正如@MarkusQ指出的那样,++运算符意味着进行就地修改。通过生成一个新对象,你就打破了这个假设。操作符重载对象是一个棘手的主题,这是一个很好的例子,说明为什么在大多数情况下最好避免它。
1 - 为了准确起见,在处理对象上的运算符时,赋值实际上不会发生在增量之前,但最终结果在这种情况下是相同的。实际上,复制原始对象引用,对原始对象执行操作,然后将复制的引用分配给左侧变量。如果您假装首先进行分配,则更容易解释。
真正发生的是:
Account b = a++;
由于++运算符如何处理对象,导致了这一点:
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
答案 2 :(得分:1)
您应该始终返回更改的值。 C#将此值用作新值,并根据操作符返回旧值或新值。