Fout: “Kan de retourwaarde niet wijzigen” c#

Ik gebruik automatisch geïmplementeerde eigenschappen.
Ik denk dat de snelste manier om het volgende op te lossen is door mijn eigen achtergrondvariabele te declareren?

public Point Origin { get; set; }
Origin.X = 10; // fails with CS1612

Foutbericht: kan de geretourneerde waarde van ‘expression’ niet wijzigen omdat
het is geen variabele

Er is geprobeerd een waardetype te wijzigen dat het resultaat was van een
tussenliggende uitdrukking. Omdat de waarde niet wordt behouden, wordt de waarde
ongewijzigd zal zijn.

Om deze fout op te lossen, slaat u het resultaat van de uitdrukking op in een
tussenliggende waarde, of gebruik een referentietype voor de tussenliggende
uitdrukking.


Antwoord 1, autoriteit 100%

Dit komt omdat Pointeen waardetype is (struct).

Hierdoor, wanneer u de eigenschap Originopent, krijgt u toegang tot een kopievan de waarde van de klasse, niet de waarde zelf zoals u zou doen met een referentietype (class), dus als u de eigenschap Xerop instelt, stelt u de eigenschap in op de kopie en verwijdert u deze, waarbij de oorspronkelijke waarde ongewijzigd blijft. Dit is waarschijnlijk niet wat je bedoelde, daarom waarschuwt de compiler je ervoor.

Als u alleen de waarde van Xwilt wijzigen, moet u zoiets als dit doen:

Origin = new Point(10, Origin.Y);

Antwoord 2, autoriteit 5%

Het gebruik van een achtergrondvariabele helpt niet.Het type Pointis een waardetype.

Je moet de hele Puntwaarde toewijzen aan de Origin-eigenschap:-

Origin = new Point(10, Origin.Y);

Het probleem is dat wanneer je de Origin-eigenschap opent, wat wordt geretourneerd door de geteen kopie is van de Puntstructuur in het automatisch aangemaakte veld Origin-eigenschappen. Vandaar dat uw wijziging van het X-veld deze kopie geen invloed heeft op het onderliggende veld. De compiler detecteert dit en geeft je een foutmelding omdat deze operatie totaal nutteloos is.

Zelfs als je je eigen backing-variabele zou gebruiken, zou je geter als volgt uitzien:-

get { return myOrigin; }

Je zou nog steeds een kopie van de puntenstructuur retourneren en je zou dezelfde foutmelding krijgen.

Hmm… als je je vraag nauwkeuriger hebt gelezen, wil je misschien de achtergrondvariabele rechtstreeks vanuit je klas wijzigen:-

myOrigin.X = 10;

Ja, dat zou je nodig hebben.


Antwoord 3, autoriteit 3%

Je weet nu al wat de oorzaak van de fout is. Als er geen constructor bestaat met een overbelasting om uw eigendom te nemen (in dit geval X), kunt u de objectinitialisatiefunctie gebruiken (die alle magie achter de schermen zal doen). Niet dat je je structs niet onveranderlijk hoeft te maken, maar geef alleen aanvullende informatie:

struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}
class MyClass
{
    public Point Origin { get; set; }
}
MyClass c = new MyClass();
c.Origin.X = 23; //fails.
//but you could do:
c.Origin = new Point { X = 23, Y = c.Origin.Y }; //though you are invoking default constructor
//instead of
c.Origin = new Point(23, c.Origin.Y); //in case there is no constructor like this.

Dit is mogelijk omdat dit achter de schermen gebeurt:

Point tmp = new Point();
tmp.X = 23;
tmp.Y = Origin.Y;
c.Origin = tmp;

Dit lijkt erg vreemd om te doen, helemaal niet aanbevolen. Gewoon een alternatieve manier opsommen. De betere manier om dit te doen is om struct onveranderlijk te maken en een goede constructor te bieden.


Antwoord 4

Afgezien van het bespreken van de voor- en nadelen van structs versus klassen, heb ik de neiging om naar het doel te kijken en het probleem vanuit dat perspectief te benaderen.

Dat gezegd hebbende, als u geen code achter de eigenschap get en set-methoden hoeft te schrijven (zoals in uw voorbeeld), zou het dan niet eenvoudiger zijn om de Origingewoon als een veld van de klasse in plaats van een eigenschap? Ik zou denken dat dit je in staat zou stellen om je doel te bereiken.

struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}
class MyClass
{
    public Point Origin;
}
MyClass c = new MyClass();
c.Origin.X = 23;   // No error.  Sets X just fine

Antwoord 5

Het probleem is dat je verwijst naar een waarde die zich op de stapel bevindt en de waarde wordt niet teruggelinkt naar de oorspronkelijke eigenschap, dus C# staat je niet toe om een verwijzing naar een waardetype terug te geven. Ik denk dat je dit kunt oplossen door de Origin-eigenschap te verwijderen en in plaats daarvan een openbaar bestand te gebruiken, ja ik weet dat het geen leuke oplossing is. De andere oplossing is om het Punt niet te gebruiken, maar in plaats daarvan je eigen Punttype als object te maken.


Antwoord 6

Ik denk dat de vangst hier is dat u probeert de subwaarden van het object in de verklaring toe te wijzen in plaats van het object zelf toe te wijzen. U moet in dit geval het volledige puntobject toewijzen, aangezien het eigenschaptype punt is.

Point newOrigin = new Point(10, 10);
Origin = newOrigin;

Ik hoop dat ik daar logisch heb gemaakt


Antwoord 7

Verwijder gewoon het pand “Get Set” als volgt, en dan werkt alles zoals altijd.

In het geval van primitieve typen gebruikt u de Get; Set; …

using Microsoft.Xna.Framework;
using System;
namespace DL
{
    [Serializable()]
    public class CameraProperty
    {
        #region [READONLY PROPERTIES]
        public static readonly string CameraPropertyVersion = "v1.00";
        #endregion [READONLY PROPERTIES]
        /// <summary>
        /// CONSTRUCTOR
        /// </summary>
        public CameraProperty() {
            // INIT
            Scrolling               = 0f;
            CameraPos               = new Vector2(0f, 0f);
        }
        #region [PROPERTIES]   
        /// <summary>
        /// Scrolling
        /// </summary>
        public float Scrolling { get; set; }
        /// <summary>
        /// Position of the camera
        /// </summary>
        public Vector2 CameraPos;
        // instead of: public Vector2 CameraPos { get; set; }
        #endregion [PROPERTIES]
    }
}      

Antwoord 8

Ik denk dat veel mensen hier in de war raken, dit specifieke probleem heeft te maken met het begrijpen dat waardetype eigenschappeneen kopie van het waardetype retourneren (zoals bij methoden en indexeerders), en waardetype veldenzijn direct toegankelijk. De volgende code doet precies wat u probeert te bereiken door rechtstreeks toegang te krijgen tot het backing-veld van de eigenschap (opmerking: het uitdrukken van een eigenschap in zijn uitgebreide vorm met een backing-veld is het equivalent van een auto-eigenschap, maar heeft het voordeel dat we in onze code kunnen direct toegang tot het achtergrondveld):

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        myClass.SetOrigin();
        Debug.Assert(myClass.Origin.X == 10); //succeeds
    }
}
class MyClass
{
    private Point _origin;
    public Point Origin
    { 
        get => _origin; 
        set => _origin = value; 
    }
    public void SetOrigin()
    {
        _origin.X = 10; //this works
        //Origin.X = 10; // fails with CS1612;
    }
}

De fout die u krijgt is een indirect gevolg van het niet begrijpen dat een eigenschap een kopie van een waardetype retourneert. Als u een kopie van een waardetype terugkrijgt en u wijst deze niet toe aan een lokale variabele, dan kunnen eventuele wijzigingen die u in die kopie aanbrengt nooit worden gelezen en daarom noemt de compiler dit een fout omdat dit niet opzettelijk kan zijn. Als we de kopie toewijzen aan een lokale variabele, kunnen we de waarde van X wijzigen, maar deze wordt alleen gewijzigd op de lokale kopie, die de compileerfout corrigeert, maar niet het gewenste effect heeft van het wijzigen van de eigenschap Origin. De volgende code illustreert dit, aangezien de compilatiefout is verdwenen, maar de debug-bewering zal mislukken:

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        myClass.SetOrigin();
        Debug.Assert(myClass.Origin.X == 10); //throws error
    }
}
class MyClass
{
    private Point _origin;
    public Point Origin
    { 
        get => _origin; 
        set => _origin = value; 
    }
    public void SetOrigin()
    {
        var origin = Origin;
        origin.X = 10; //this is only changing the value of the local copy
    }
}

Other episodes