J'utilise des propriétés implémentées automatiquement. Je suppose que le moyen le plus rapide de résoudre le problème suivant est de déclarer ma propre variable de sauvegarde?
public Point Origin { get; set; }
Origin.X = 10; // fails with CS1612
Message d'erreur: impossible de modifier la valeur de retour de 'expression' car ce n'est pas une variable
Tentative de modification d'un type de valeur résultant d'une expression intermédiaire. Étant donné que la valeur n'est pas persistante, la valeur restera inchangée.
Pour résoudre cette erreur, stockez le résultat de l'expression dans une valeur intermédiaire ou utilisez un type de référence pour l'expression intermédiaire.
Réponses:
C'est parce que
Point
c'est un type valeur (struct
).Pour cette raison, lorsque vous accédez à la
Origin
propriété, vous accédez à une copie de la valeur détenue par la classe, pas à la valeur elle-même comme vous le feriez avec un type de référence (class
), donc si vous définissez laX
propriété dessus, vous définissez la propriété sur la copie, puis en la supprimant, en laissant la valeur d'origine inchangée. Ce n'est probablement pas ce que vous vouliez, c'est pourquoi le compilateur vous en avertit.Si vous souhaitez modifier uniquement la
X
valeur, vous devez faire quelque chose comme ceci:la source
L'utilisation d'une variable de sauvegarde n'aidera pas.LePoint
type est un type Value.Vous devez attribuer toute la valeur Point à la propriété Origin: -
Le problème est que lorsque vous accédez à la propriété Origin, ce qui est renvoyé par le
get
est une copie de la structure Point dans le champ créé automatiquement par les propriétés Origin. Par conséquent, votre modification du champ X cette copie n'affecterait pas le champ sous-jacent. Le compilateur détecte cela et vous donne une erreur car cette opération est totalement inutile.Même si vous utilisiez votre propre variable de sauvegarde,
get
cela ressemblerait à: -Vous retourneriez toujours une copie de la structure Point et vous obtiendriez la même erreur.
Hmm ... après avoir lu votre question plus attentivement, peut-être voulez-vous en fait modifier la variable de sauvegarde directement depuis votre classe: -
Oui, ce serait ce dont vous auriez besoin.
la source
À présent, vous savez déjà quelle est la source de l'erreur. Dans le cas où un constructeur n'existe pas avec une surcharge pour prendre votre propriété (dans ce cas
X
), vous pouvez utiliser l'initialiseur d'objet (qui fera toute la magie dans les coulisses). Non pas que vous n'ayez pas besoin de rendre vos structures immuables , mais simplement de donner des informations supplémentaires:Ceci est possible car dans les coulisses cela se produit:
Cela semble être une chose très étrange à faire, pas du tout recommandée. Juste énumérer une autre manière. La meilleure façon de faire est de rendre struct immuable et de fournir un constructeur approprié.
la source
Origin.Y
? Étant donné une propriété de typePoint
, je pense que la manière idiomatique de changerX
serait simplementvar temp=thing.Origin; temp.X = 23; thing.Origin = temp;
. L'approche idiomatique a l'avantage de ne pas avoir à mentionner les membres qu'elle ne veut pas modifier, une fonctionnalité qui n'est possible que parce qu'ellePoint
est mutable. Je suis perplexe devant la philosophie qui dit cela parce que le compilateur ne peut pas permettre que l'Origin.X = 23;
on doive concevoir une structure pour exiger du code commeOrigin.X = new Point(23, Origin.Y);
. Ce dernier me semble vraiment dégueulasse.X
etY
à un constructeur spécifique). Maintenant, il perd le point quand on peut le fairePoint p = new Point()
. Je sais pourquoi c'est vraiment nécessaire pour une structure, donc inutile d'y penser. Mais avez-vous une bonne idée de mettre à jour une seule propriété commeX
?Object
. Ils ne le font pas. Chaque définition de type valeur définit en fait deux types de choses: un type d'emplacement de stockage (utilisé pour les variables, les emplacements de tableau, etc.) et un type d'objet de tas, parfois appelé type «en boîte» (utilisé lorsqu'une valeur de type valeur est stocké dans un emplacement de type référence).En plus de débattre des avantages et des inconvénients des structures par rapport aux classes, j'ai tendance à regarder l'objectif et à aborder le problème de cette perspective.
Cela étant dit, si vous n'avez pas besoin d'écrire du code derrière les méthodes get et set de la propriété (comme dans votre exemple), ne serait-il pas plus facile de simplement déclarer le
Origin
comme un champ de la classe plutôt qu'une propriété? Je devrais penser que cela vous permettrait d'atteindre votre objectif.la source
Le problème est que vous pointez sur une valeur située sur la pile et que la valeur ne sera pas relfectée vers la propriété orignal, donc C # ne vous permet pas de renvoyer une référence à un type valeur. Je pense que vous pouvez résoudre ce problème en supprimant la propriété Origin et en utilisant à la place un fichier public, oui je sais que ce n'est pas une bonne solution. L'autre solution consiste à ne pas utiliser le point, mais à créer votre propre type de point en tant qu'objet.
la source
Point
est un membre d'un type de référence, il ne sera pas sur la pile, il sera sur le tas dans la mémoire de l'objet conteneur.Je suppose que le hic ici est que vous essayez d'attribuer des sous-valeurs d'objet dans l'instruction plutôt que d'attribuer l'objet lui-même. Dans ce cas, vous devez affecter l'objet Point entier car le type de propriété est Point.
J'espère que j'ai eu du sens là-bas
la source
Point
s'agissait d'un type de classe mutable, le code d'origine aurait défini un champ ou une propriétéX
dans l'objet retourné par propriétéOrigin
. Je ne vois aucune raison de croire que cela aurait l'effet désiré sur l'objet contenant laOrigin
propriété. Certaines classes Framework ont des propriétés qui copient leur état dans de nouvelles instances de classe mutable et les renvoient. Une telle conception a l'avantage de permettre au code commething1.Origin = thing2.Origin;
de définir l'état d'origine de l'objet pour qu'il corresponde à celui d'un autre, mais il ne peut pas avertir du code commething1.Origin.X += 4;
.Supprimez simplement la propriété "get set" comme suit, puis tout fonctionne comme toujours.
Dans le cas des types primitifs, utilisez la commande get; set; ...
la source
Je pense que beaucoup de gens sont confus ici, ce problème particulier est lié à la compréhension que les propriétés de type valeur renvoient une copie du type valeur (comme avec les méthodes et les indexeurs) et que les champs de type valeur sont accessibles directement . Le code suivant fait exactement ce que vous essayez d'obtenir en accédant directement au champ de sauvegarde de la propriété (note: exprimer une propriété sous sa forme verbeuse avec un champ de sauvegarde est l'équivalent d'une propriété automatique, mais présente l'avantage que dans notre code, nous pouvons accéder directement au champ de support):
L'erreur que vous obtenez est une conséquence indirecte de ne pas comprendre qu'une propriété renvoie une copie d'un type valeur. Si vous recevez une copie d'un type valeur et que vous ne l'assignez pas à une variable locale, toutes les modifications que vous apportez à cette copie ne peuvent jamais être lues et par conséquent, le compilateur génère cela comme une erreur car cela ne peut pas être intentionnel. Si nous affectons la copie à une variable locale, nous pouvons modifier la valeur de X, mais elle ne sera modifiée que sur la copie locale, ce qui corrige l'erreur de compilation, mais n'aura pas l'effet souhaité de modifier la propriété Origin. Le code suivant illustre cela, car l'erreur de compilation a disparu, mais l'assertion de débogage échouera:
la source