后增量运算符重载

kie*_*wic 16 c# operator-overloading

我在尝试在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
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试使用类时,它看起来像是交换对象.

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"
    }
}
Run Code Online (Sandbox Code Playgroud)

调试应用程序,重载的operator方法,返回具有旧值(10)的新对象和通过引用传递的对象具有新值(11),但最后交换对象.为什么会这样?

Mar*_*usQ 14

我的第一个想法是指出++的正常语义是就地修改.如果你想模仿你写的:

public static Account operator ++(Account a)
{
    a.Balance += 1;
    return a;
}
Run Code Online (Sandbox Code Playgroud)

而不是创建一个新对象.

但后来我意识到你试图模仿后期增量.

所以我的第二个想法是"不要这样做" - 语义根本不能很好地映射到对象上,因为"使用"的值实际上是一个可变的存储位置.但没有人喜欢被一个随机的陌生人告知"不要那样做",所以我会让微软告诉你不要这样做.我担心他们的话在这些问题上是最终的.

PS至于为什么它正在做它所做的事情,你真的重写了preincrement运算符,然后使用它就好像它是postincrement运算符.

  • 页面不公开,并且不存在:-)只是我的运气......你还记得标题,还是要找什么? (5认同)

Jer*_*ine 12

关键是要了解线路的Account b = a++;工作原理.鉴于您的代码是如何编写的,这一行等同于:

Account b = a;
a++;
Run Code Online (Sandbox Code Playgroud)

这就是它将执行的顺序.有效的赋值(1)在增量之前发生.所以,这一行的第一个效果是ab都引用原始对象a.

现在将评估++部分.在operator方法的内部,我们递增Balance原始对象.此时ab都指向原始,a Balance为11,b将继续这样做.

但是,您已在operator方法内创建了一个新对象,并将其作为运算符的输出返回.一个将立即更新在新创建的对象指向.

所以,一个现在指向一个新的对象,而b继续指向原始.这就是WriteLine输出出现交换的原因.

正如@MarkusQ指出的那样,++运算符意味着进行就地修改.通过生成一个新对象,你就打破了这个假设.操作员在对象上重载是一个棘手的主题,这是一个很好的例子,说明为什么在大多数情况下更好地避免它.


1 - 为了准确起见,在处理对象上的运算符时,赋值实际上不会发生在增量之前,但最终结果在这种情况下是相同的.实际上,复制原始对象引用,对原始对象执行操作,然后将复制的引用分配给左侧变量.如果你假装首先发生任务,那就更容易解释一下.

真正发生的是这个:

Account b = a++;
Run Code Online (Sandbox Code Playgroud)

由于++运算符如何处理对象,导致了这种情况:

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
Run Code Online (Sandbox Code Playgroud)