Estoy usando propiedades implementadas automáticamente. ¿Supongo que la forma más rápida de arreglar lo siguiente es declarar mi propia variable de respaldo?
public Point Origin { get; set; }
Origin.X = 10; // fails with CS1612
Mensaje de error: no se puede modificar el valor de retorno de 'expresión' porque no es una variable
Se intentó modificar un tipo de valor que fue el resultado de una expresión intermedia. Debido a que el valor no persiste, el valor no cambiará.
Para resolver este error, almacene el resultado de la expresión en un valor intermedio o use un tipo de referencia para la expresión intermedia.
c#
variables
struct
immutability
Pablo
fuente
fuente
Respuestas:
Esto se debe a que
Point
es un tipo de valor (struct
).Debido a esto, cuando accede a la
Origin
propiedad está accediendo a una copia del valor que posee la clase, no al valor en sí como lo haría con un tipo de referencia (class
), por lo que si establece el valorX
propiedad en él, entonces está configurando la propiedad en la copia y luego descartarla, dejando el valor original sin cambios. Probablemente esto no sea lo que pretendía, por eso el compilador le está advirtiendo al respecto.Si desea cambiar solo el
X
valor, debe hacer algo como esto:fuente
Usar una variable de respaldo no ayudará.ElPoint
tipo es un tipo de valor.Debe asignar todo el valor del Punto a la propiedad Origen: -
El problema es que cuando accede a la propiedad Origen, lo que devuelve el
get
es una copia de la estructura Punto en el campo creado automáticamente de las propiedades Origen. Por lo tanto, su modificación del campo X esta copia no afectaría el campo subyacente. El compilador detecta esto y le da un error ya que esta operación es completamente inútil.Incluso si usara su propia variable de respaldo,
get
se vería así:Seguiría devolviendo una copia de la estructura de puntos y obtendría el mismo error.
Hmm ... habiendo leído tu pregunta con más cuidado, tal vez realmente quieras modificar la variable de respaldo directamente desde tu clase:
Sí, eso sería lo que necesitarías.
fuente
Por ahora ya sabes cuál es la fuente del error. En caso de que no exista un constructor con una sobrecarga para tomar su propiedad (en este caso
X
), puede usar el inicializador de objetos (que hará toda la magia detrás de escena). No es que no necesite hacer que sus estructuras sean inmutables , sino solo dar información adicional:Esto es posible porque detrás de escena sucede esto:
Esto parece una cosa muy extraña, no se recomienda en absoluto. Solo enumerando una forma alternativa. La mejor manera de hacerlo es hacer que struct sea inmutable y proporcionar un constructor adecuado.
fuente
Origin.Y
? Dada una propiedad de tipoPoint
, creo que la forma idiomática de cambiarX
seríavar temp=thing.Origin; temp.X = 23; thing.Origin = temp;
. El enfoque idiomático tiene la ventaja de que no tiene que mencionar a los miembros que no desea modificar, una característica que solo es posible porquePoint
es mutable. Estoy desconcertado con la filosofía que dice que debido a que el compilador no puede permitir queOrigin.X = 23;
uno deba diseñar una estructura que requiera un código similarOrigin.X = new Point(23, Origin.Y);
. Esto último me parece realmente asqueroso.X
yY
al constructor específico). Ahora pierde el punto cuando uno puede hacerPoint p = new Point()
. Sé por qué es realmente necesario para una estructura, así que no tiene sentido pensar en ello. ¿Pero tienes una idea genial para actualizar solo una propiedadX
?Object
. Ellos no. Cada definición de tipo de valor en realidad define dos tipos de cosas: un tipo de ubicación de almacenamiento (utilizado para variables, ranuras de matriz, etc.) y un tipo de objeto de almacenamiento dinámico, a veces denominado tipo "en caja" (utilizado cuando un valor de tipo de valor se almacena en una ubicación de tipo de referencia).Además de debatir los pros y los contras de las estructuras versus las clases, tiendo a mirar el objetivo y abordar el problema desde esa perspectiva.
Dicho esto, si no necesita escribir código detrás de los métodos get y set de la propiedad (como en su ejemplo), ¿no sería más fácil simplemente declararlo
Origin
como un campo de la clase en lugar de una propiedad? Creo que esto te permitiría lograr tu objetivo.fuente
El problema es que apunta a un valor ubicado en la pila y el valor no se volverá a proteger a la propiedad original, por lo que C # no le permite devolver una referencia a un tipo de valor. Creo que puede resolver esto eliminando la propiedad Origin y en su lugar usar un archivo público, sí, sé que no es una buena solución. La otra solución es no usar el Punto y, en su lugar, crear su propio tipo de Punto como objeto.
fuente
Point
es un miembro de un tipo de referencia, entonces no estará en la pila, estará en el montón en la memoria del objeto que lo contiene.Supongo que el problema aquí es que estás tratando de asignar subvalores de objeto en la declaración en lugar de asignar el objeto en sí. Debe asignar todo el objeto Point en este caso, ya que el tipo de propiedad es Point.
Espero tener sentido allí
fuente
Point
fuera un tipo de clase mutable, el código original habría establecido un campo o propiedadX
en el objeto devuelto por la propiedadOrigin
. No veo ninguna razón para creer que tendría el efecto deseado sobre el objeto que contiene laOrigin
propiedad. Algunas clases de Framework tienen propiedades que copian su estado en nuevas instancias de clase mutable y las devuelven. Tal diseño tiene la ventaja de permitir que el códigothing1.Origin = thing2.Origin;
establezca el estado del origen del objeto para que coincida con el de otro, pero no puede advertir sobre el códigothing1.Origin.X += 4;
.Simplemente elimine la propiedad "get set" de la siguiente manera, y luego todo funciona como siempre.
En el caso de los tipos primitivos instread, use get; set; ...
fuente
Creo que mucha gente se está confundiendo aquí, este problema en particular está relacionado con la comprensión de que las propiedades de tipo de valor devuelven una copia del tipo de valor (como con los métodos e indexadores), y se accede directamente a los campos de tipo de valor . El siguiente código hace exactamente lo que está tratando de lograr accediendo directamente al campo de respaldo de la propiedad (nota: expresar una propiedad en su forma detallada con un campo de respaldo es el equivalente de una propiedad automática, pero tiene la ventaja de que en nuestro código podemos acceder al campo de respaldo directamente):
El error que está obteniendo es una consecuencia indirecta de no entender que una propiedad devuelve una copia de un tipo de valor. Si se le devuelve una copia de un tipo de valor y no la asigna a una variable local, los cambios que realice en esa copia nunca podrán leerse y, por lo tanto, el compilador plantea esto como un error, ya que esto no puede ser intencional. Si asignamos la copia a una variable local, entonces podemos cambiar el valor de X, pero solo se cambiará en la copia local, que corrige el error de tiempo de compilación, pero no tendrá el efecto deseado de modificar la propiedad Origen. El siguiente código ilustra esto, ya que el error de compilación desapareció, pero la afirmación de depuración fallará:
fuente