Ich verwende automatisch implementierte Eigenschaften. Ich denke, der schnellste Weg, um Folgendes zu beheben, besteht darin, meine eigene Hintergrundvariable zu deklarieren.
public Point Origin { get; set; }
Origin.X = 10; // fails with CS1612
Fehlermeldung: Der Rückgabewert von 'Ausdruck' kann nicht geändert werden, da es sich nicht um eine Variable handelt
Es wurde versucht, einen Werttyp zu ändern, der das Ergebnis eines Zwischenausdrucks war. Da der Wert nicht beibehalten wird, bleibt der Wert unverändert.
Um diesen Fehler zu beheben, speichern Sie das Ergebnis des Ausdrucks in einem Zwischenwert oder verwenden Sie einen Referenztyp für den Zwischenausdruck.
c#
variables
struct
immutability
P aul
quelle
quelle
Antworten:
Dies liegt daran, dass
Point
es sich um einen Werttyp (struct
) handelt.Wenn Sie auf die
Origin
Eigenschaft zugreifen , greifen Sie daher auf eine Kopie des Werts zu, der von der Klasse gehalten wird, und nicht auf den Wert selbst, wie Sie es mit einem Referenztyp (class
) tun würden. Wenn Sie also dieX
Eigenschaft darauf festlegen, legen Sie fest die Eigenschaft auf der Kopie und verwerfen Sie sie dann, wobei der ursprüngliche Wert unverändert bleibt. Dies ist wahrscheinlich nicht das, was Sie beabsichtigt haben, weshalb der Compiler Sie davor warnt.Wenn Sie nur den
X
Wert ändern möchten , müssen Sie Folgendes tun:quelle
Die Verwendung einer Hintergrundvariablen hilft nicht weiter.DerPoint
Typ ist ein Werttyp.Sie müssen der Origin-Eigenschaft den gesamten Punktwert zuweisen: -
Das Problem besteht darin, dass beim Zugriff auf die Origin-Eigenschaft
get
eine Kopie der Punktstruktur im automatisch erstellten Feld Origin-Eigenschaften zurückgegeben wird. Daher würde Ihre Änderung des X-Felds dieser Kopie das zugrunde liegende Feld nicht beeinflussen. Der Compiler erkennt dies und gibt einen Fehler aus, da dieser Vorgang völlig nutzlos ist.Selbst wenn Sie Ihre eigene Hintergrundvariable verwenden
get
würden, würde dies folgendermaßen aussehen:Sie würden immer noch eine Kopie der Punktstruktur zurückgeben und würden den gleichen Fehler erhalten.
Hmm ... nachdem Sie Ihre Frage genauer gelesen haben, möchten Sie vielleicht tatsächlich die Hintergrundvariable direkt aus Ihrer Klasse heraus ändern: -
Ja, das wäre was du brauchst.
quelle
Inzwischen wissen Sie bereits, woher der Fehler stammt. Falls es keinen Konstruktor mit einer Überladung gibt, um Ihre Eigenschaft zu übernehmen (in diesem Fall
X
), können Sie den Objektinitialisierer verwenden (der die ganze Magie hinter den Kulissen ausführt). Nicht, dass Sie Ihre Strukturen nicht unveränderlich machen müssen , sondern nur zusätzliche Informationen geben müssen:Dies ist möglich, weil dies hinter den Kulissen geschieht:
Dies scheint eine sehr seltsame Sache zu sein, die überhaupt nicht empfohlen wird. Listen Sie einfach einen alternativen Weg auf. Der bessere Weg ist, struct unveränderlich zu machen und einen geeigneten Konstruktor bereitzustellen.
quelle
Origin.Y
wegwerfen? Bei einer Eigenschaft vom TypPoint
würde ich denken, dass der idiomatische Weg, sich zu ändern, einfachX
wärevar temp=thing.Origin; temp.X = 23; thing.Origin = temp;
. Der idiomatische Ansatz hat den Vorteil, dass die Mitglieder, die nicht geändert werden sollen, nicht erwähnt werden müssen. Diese Funktion ist nur möglich, weil siePoint
veränderbar ist. Ich bin verwirrt über die Philosophie, die besagt, dass man, weil der Compiler es nicht zulassen kann,Origin.X = 23;
eine Struktur entwerfen sollte, die Code wie erfordertOrigin.X = new Point(23, Origin.Y);
. Letzteres scheint mir wirklich eklig zu sein.X
undY
einen bestimmten Konstruktor übergeben). Jetzt verliert es den Punkt, an dem man es tun kannPoint p = new Point()
. Ich weiß, warum es wirklich für eine Struktur erforderlich ist, also macht es keinen Sinn, darüber nachzudenken. Aber haben Sie eine coole Idee, nur eine Eigenschaft wie zu aktualisierenX
?Object
. Sie tun es nicht. Jede Werttypdefinition definiert tatsächlich zwei Arten von Dingen: einen Speicherorttyp (der für Variablen, Array-Slots usw. verwendet wird) und einen Heap-Objekttyp, der manchmal als "Boxed" -Typ bezeichnet wird (wird verwendet, wenn ein Werttypwert verwendet wird) wird an einem Ort vom Referenztyp gespeichert).Abgesehen von der Debatte über die Vor- und Nachteile von Strukturen gegenüber Klassen neige ich dazu, das Ziel zu betrachten und das Problem aus dieser Perspektive zu betrachten.
Wenn Sie jedoch keinen Code hinter die get- und set-Methoden der Eigenschaft schreiben müssen (wie in Ihrem Beispiel), ist es dann nicht einfacher, die einfach
Origin
als Feld der Klasse und nicht als Eigenschaft zu deklarieren ? Ich sollte denken, dies würde es Ihnen ermöglichen, Ihr Ziel zu erreichen.quelle
Das Problem besteht darin, dass Sie auf einen Wert im Stapel verweisen und der Wert nicht an die Eigenschaft orignal zurückgegeben wird, sodass Sie mit C # keinen Verweis auf einen Werttyp zurückgeben können. Ich denke, Sie können dies lösen, indem Sie die Origin-Eigenschaft entfernen und stattdessen eine öffentliche Datei verwenden. Ja, ich weiß, dass dies keine gute Lösung ist. Die andere Lösung besteht darin, den Punkt nicht zu verwenden und stattdessen Ihren eigenen Punkttyp als Objekt zu erstellen.
quelle
Point
Mitglied eines Referenztyps ist, befindet es sich nicht auf dem Stapel, sondern auf dem Heap im Speicher des enthaltenen Objekts.Ich denke, der Haken hier ist, dass Sie versuchen, die Unterwerte des Objekts in der Anweisung zuzuweisen, anstatt das Objekt selbst zuzuweisen. In diesem Fall müssen Sie das gesamte Point-Objekt zuweisen, da der Eigenschaftstyp Point ist.
Hoffe ich habe dort Sinn gemacht
quelle
Point
ein veränderbarer Klassentyp wäre, hätte der ursprüngliche Code ein Feld oder eine EigenschaftX
in dem von der Eigenschaft zurückgegebenen Objekt festgelegtOrigin
. Ich sehe keinen Grund zu der Annahme, dass dies die gewünschte Wirkung auf das Objekt haben würde, das dieOrigin
Eigenschaft enthält. Einige Framework-Klassen verfügen über Eigenschaften, die ihren Status in neue veränderbare Klasseninstanzen kopieren und diese zurückgeben. Ein solches Design hat den Vorteil, dass Codething1.Origin = thing2.Origin;
den Status des Ursprungs des Objekts so einstellen kann, dass er mit dem eines anderen übereinstimmt, aber nicht vor Code wie warnen kannthing1.Origin.X += 4;
.Entfernen Sie einfach die Eigenschaft "get set" wie folgt, und dann funktioniert alles wie immer.
Bei primitiven Typen instread verwenden Sie get; set; ...
quelle
Ich denke , eine Menge Leute hier sind verwirrt zu werden , dieses besondere Problem bezieht sich auf das Verständnis , dass Werttyp Eigenschaften eine Kopie des Werttyp zurückgeben (wie bei Methoden und Indexer) und Werttyp Felder werden direkt zugegriffen . Der folgende Code macht genau das, was Sie erreichen möchten, indem Sie direkt auf das Hintergrundfeld der Eigenschaft zugreifen (Hinweis: Das Ausdrücken einer Eigenschaft in ihrer ausführlichen Form mit einem Hintergrundfeld entspricht einer Auto-Eigenschaft, hat jedoch den Vorteil, dass dies in unserem Code möglich ist direkt auf das Hintergrundfeld zugreifen):
Der Fehler, den Sie erhalten, ist eine indirekte Folge des Nichtverständnisses, dass eine Eigenschaft eine Kopie eines Werttyps zurückgibt. Wenn Sie eine Kopie eines Werttyps zurückgeben und diese keiner lokalen Variablen zuweisen, können alle Änderungen, die Sie an dieser Kopie vornehmen, niemals gelesen werden. Der Compiler gibt dies daher als Fehler aus, da dies nicht beabsichtigt sein kann. Wenn wir die Kopie einer lokalen Variablen zuweisen, können wir den Wert von X ändern. Er wird jedoch nur für die lokale Kopie geändert, wodurch der Fehler bei der Kompilierung behoben wird, die gewünschte Origin-Eigenschaft jedoch nicht geändert wird. Der folgende Code veranschaulicht dies, da der Kompilierungsfehler behoben ist, die Debug-Bestätigung jedoch fehlschlägt:
quelle