19 Stimmen

Post-Inkrement-Operator-Überladung

Ich habe Probleme beim Versuch, den Post-Inkrement-Operator in C# zu überladen. Mit Ganzzahlen erhalten wir die folgenden Ergebnisse.

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

Aber wenn ich es mit Klassen versuche, sieht es so aus, als ob die Objekte ausgetauscht werden.

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"
    }
}

Beim Debuggen der Anwendung gibt die überladene Operator-Methode das neue Objekt mit dem alten Wert (10) und das Objekt, das als Referenz übergeben wurde, mit dem neuen Wert (11) zurück, aber schließlich werden die Objekte ausgetauscht. Warum geschieht dies?

18voto

MarkusQ Punkte 21488

Mein erster Gedanke war, darauf hinzuweisen, dass die normale Semantik von ++ wie folgt lautet In-Place-Modifikation . Wenn Sie das nachahmen wollen, müssen Sie schreiben:

public static Account operator ++(Account a)
{
    a.Balance += 1;
    return a;
}

und nicht ein neues Objekt erstellen.

Aber dann habe ich gemerkt, dass du versuchst, das Postwachstum zu imitieren.

Mein zweiter Gedanke ist: "Tu das nicht" - die Semantik lässt sich nicht gut auf Objekte abbilden, da der Wert, der "verwendet" wird, in Wirklichkeit ein veränderlicher Speicherplatz ist. Aber niemand mag es, von einem zufälligen Fremden gesagt zu bekommen, "tu das nicht", also lasse ich es. Microsoft sagt, Sie sollen es nicht tun . Und ich fürchte, ihr Wort ist in solchen Angelegenheiten endgültig.

P.S. Zum Thema warum Sie überschreiben den Preincrement-Operator und verwenden ihn dann als ob es wäre der Postinkrement-Operator.

13voto

Jeromy Irvine Punkte 11205

Der Schlüssel liegt darin, zu verstehen, wie die Linie Account b = a++; Werke. Wenn man bedenkt, wie Ihr Code geschrieben ist, entspricht diese Zeile dem hier:

Account b = a;
a++;

Und das ist die Reihenfolge, in der sie ausgeführt wird. Die Zuweisung effektiv(1) erfolgt vor dem Inkrement. Die erste Auswirkung dieser Zeile ist also, dass a y b verweisen beide auf das ursprüngliche Objekt a .

Nun wird der Teil ++ ausgewertet. Innerhalb der Operator-Methode erhöhen wir die Balance des ursprünglichen Objekts. An diesem Punkt a y b sind beide auf das Original gerichtet, mit einem Balance von 11, und b wird dies auch weiterhin tun.

Sie haben jedoch ein neues Objekt innerhalb der Operator-Methode erstellt und es als Ausgabe des Operators zurückgegeben. a wird nun aktualisiert und verweist auf das neu erstellte Objekt.

Also, a zeigt nun auf ein neues Objekt, während b verweist weiterhin auf das Original. Aus diesem Grund erscheint die WriteLine-Ausgabe vertauscht.

Wie @MarkusQ schon sagte, ist der ++-Operator dazu gedacht, Änderungen an Ort und Stelle vorzunehmen. Indem Sie ein neues Objekt erzeugen, brechen Sie diese Annahme. Die Überladung von Objekten mit Operatoren ist ein heikles Thema, und dies ist ein hervorragendes Beispiel dafür, warum es in den meisten Fällen besser vermieden wird.


1 - Der Genauigkeit halber sei erwähnt, dass die Zuweisung nicht vor dem Inkrement erfolgt, wenn es sich um Operatoren für Objekte handelt, aber das Endergebnis ist in diesem Fall das gleiche. In Wirklichkeit wird die ursprüngliche Objektreferenz kopiert, die Operation wird auf dem Original ausgeführt, und dann wird die kopierte Referenz der linken Variablen zugewiesen. Es ist nur einfacher zu erklären, wenn Sie so tun, als ob die Zuweisung zuerst erfolgt.

Was wirklich passiert ist, ist, dass dies:

Account b = a++;

führt aufgrund der Funktionsweise des Operators ++ bei Objekten zu diesem Ergebnis:

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

1voto

Sie sollten immer den geänderten Wert zurückgeben. C# verwendet dies als neuen Wert und gibt je nach Operator den alten oder neuen Wert zurück.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X