177 votes

Erreur "Impossible de modifier la valeur de retour" c#

J'utilise des propriétés auto-implémentées. Je suppose que le moyen le plus rapide de remédier à ce problème est de déclarer ma propre variable de support ?

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

Une tentative a été faite pour modifier un type de valeur qui était le résultat d'une expression intermédiaire. Comme la valeur n'est pas persistée, elle 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.

14 votes

C'est une autre illustration de la raison pour laquelle les types de valeurs mutables sont une mauvaise idée. Si vous pouvez éviter de muter un type de valeur, faites-le.

0 votes

Prenez le code suivant (issu de mes efforts d'implémentation d'AStar blogué par un certain EL :-), qui ne pouvait pas éviter de changer un type de valeur : class Path<T> : IEnumerable<T> où T : INode, new() {...} public HexNode(int x, int y) : this(new Point(x, y)) {} Path<T> path = new Path<T>(new T(x, y)) ; // Error // Ugly fix Path<T> path = new Path<T>(new T()) ; path.LastStep.Centre = new Point(x, y) ;

224voto

Greg Beech Points 55270

Cela s'explique par le fait que Point est un type de valeur ( struct ).

Pour cette raison, lorsque vous accédez au Origin vous accédez à une propriété copie de la valeur détenue par la classe, et non la valeur elle-même, comme c'est le cas avec un type de référence ( class ), donc si vous définissez l'option X alors vous définissez la propriété sur la copie, puis vous la supprimez, laissant la valeur originale inchangée. Ce n'est probablement pas ce que vous vouliez, et c'est pourquoi le compilateur vous en avertit.

Si vous souhaitez modifier uniquement le X vous devez faire quelque chose comme ceci :

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

2 votes

@Paul : Avez-vous la possibilité de changer le struct en une classe ?

2 votes

C'est un peu dommage, car l'assignation de la propriété a un effet secondaire (la structure agit comme une vue dans un type de référence de soutien).

0 votes

Une autre solution consiste à transformer simplement votre structure en une classe. Contrairement à ce qui se passe en C++, où une classe et un struct ne diffèrent que par l'accès aux membres par défaut (respectivement privé et public), les structs et les classes en C# présentent quelques différences supplémentaires. Voici quelques informations supplémentaires : docs.microsoft.com/fr/us/dotnet/csharp/programming-guide/

11voto

AnthonyWJones Points 122520

L'utilisation d'une variable de soutien ne sera pas utile. Le site Point est un type de valeur.

Vous devez attribuer la valeur entière du point à la propriété Origin:-

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

Le problème est que, lorsque vous accédez à la propriété Origin, ce qui est renvoyé par la fonction get est une copie de la structure du point dans le champ de création automatique des propriétés de l'origine. Par conséquent, si vous modifiez le champ X, cette copie n'affectera pas le champ sous-jacent. Le compilateur détecte cela et vous donne une erreur puisque cette opération est totalement inutile.

Même si vous avez utilisé votre propre variable de support votre get ressemblerait à :-

get { return myOrigin; }

Vous renverriez 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 d'accompagnement directement à l'intérieur de votre classe : -.

myOrigin.X = 10;

Oui, c'est ce dont vous auriez besoin.

7voto

nawfal Points 13500

A 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-ci X ), vous pouvez utiliser l'initialisateur d'objet (qui fera toute la magie en coulisse). Non pas que vous ne devez pas rendre vos structs immuables mais je donne juste des informations supplémentaires :

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 = Origin.Y }; //though u r invoking default ctor

//instead of
c.Origin = new Point(23, Origin.Y); //in case there is no constructor like this.

Cela est possible parce que, dans les coulisses, ceci se produit :

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

Cela semble être une chose très étrange à faire, pas du tout recommandé. Il s'agit simplement d'une liste de solutions alternatives. La meilleure façon de faire est de rendre la structure immuable et de fournir un constructeur approprié.

2 votes

Est-ce que ça ne réduirait pas à néant la valeur de Origin.Y ? Étant donné une propriété de type Point je pense que la façon idiomatique de changer juste X serait var 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 caractéristique qui n'est possible que parce que Point est mutable. Je suis perplexe sur la philosophie qui dit que parce que le compilateur ne peut pas autoriser Origin.X = 23; il faut concevoir une structure pour exiger un code comme Origin.X = new Point(23, Origin.Y); . Ce dernier point me semble vraiment dégoûtant.

0 votes

@supercat c'est la première fois que je pense à votre remarque, Cela a beaucoup de sens ! Avez-vous une autre idée de modèle/conception pour résoudre ce problème ? Cela aurait été plus facile si C# n'avait pas fourni le constructeur par défaut pour un struct par défaut (dans ce cas, je dois strictement passer les deux éléments suivants X et Y au constructeur spécifique). Maintenant il perd le point quand on peut faire Point p = new Point() . Je sais pourquoi c'est vraiment nécessaire pour une structure, donc pas la peine d'y penser. Mais avez-vous une idée pour mettre à jour une seule propriété, par exemple X ?

0 votes

Pour les structures qui encapsulent une collection de variables indépendantes mais liées (comme les coordonnées d'un point), je préfère que la structure expose simplement tous ses membres comme des champs publics ; pour modifier un membre d'une propriété de structure, il suffit de la lire, de modifier le membre et de la réécrire. Il aurait été intéressant que C# fournisse une déclaration "simple Plain-Old-Data-Struct" qui définirait automatiquement un constructeur dont la liste des paramètres correspondrait à la liste des champs, mais les responsables de C# méprisent les structures mutables.

0voto

MSIL Points 585

Je suppose que le problème est que vous essayez d'assigner les sous-valeurs de l'objet dans l'instruction plutôt que d'assigner l'objet lui-même. Dans ce cas, vous devez assigner l'objet Point dans son intégralité, car le type de propriété est Point.

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

J'espère avoir été clair.

2 votes

Le point important est que Point est une structure (valuetype). S'il s'agissait d'une classe (objet), le code original aurait fonctionné.

0 votes

@HansKesting : Si Point était un type de classe mutable, le code original aurait défini le champ ou la propriété X dans l'objet retourné par la propriété Origin . Je n'ai aucune raison de croire que cela aurait l'effet désiré sur l'objet contenant le Origin propriété. Certaines classes du Framework ont des propriétés qui copient leur état vers de nouvelles instances de classes mutables et les renvoient. Une telle conception a l'avantage de permettre à du code tel que thing1.Origin = thing2.Origin; pour définir l'état de l'origine d'un objet pour qu'il corresponde à celui d'un autre, mais il ne peut pas avertir sur du code comme thing1.Origin.X += 4; .

0voto

Fredrik Normén Points 19

Le problème est que vous pointez vers une valeur située sur la pile et la valeur ne sera pas retransmise à la propriété orignal, donc C# ne vous permet pas de retourner une référence à un type de 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 solution agréable. L'autre solution est de ne pas utiliser le point, et de créer votre propre type de point en tant qu'objet.

1 votes

Si le Point est un membre d'un type de référence, il ne sera pas sur la pile, mais sur le tas, dans la mémoire de l'objet qui le contient.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X