que propiedad property espaƱol convert c#

c# - propiedad - Tipos de valores const, readonly y mutables



readonly property c# (6)

El compilador simplemente no tiene suficiente información disponible sobre un método para saber que el método muta la estructura. Un método bien puede tener un efecto secundario que es útil, pero no cambia a ningún miembro de la estructura. Si técnicamente sería posible agregar dicho análisis al compilador. Pero eso no funcionará para ningún tipo que viva en otro conjunto.

El ingrediente que falta es un token de metadatos que indica que un método no muta a ningún miembro. Como la palabra clave const en C ++. No disponible. Habría sido drásticamente no compatible con CLS si se hubiera agregado en el diseño original. Hay muy pocos idiomas que apoyan la noción. Solo puedo pensar en C ++ pero no salgo mucho.

Fwiw, el compilador genera código explícito para asegurar que la declaración no pueda modificar accidentalmente la lectura solo. Esta declaración

staticReadOnlyPoint.Offset(1, 1);

se traduce a

Point temp = staticReadOnlyPoint; // makes a copy temp.Offset(1, 1);

Agregar código que luego compara el valor y genera un error de tiempo de ejecución también es solo técnicamente posible. Esto cuesta mucho.

Continúo mi estudio de C # y la especificación del lenguaje y aquí va otro comportamiento que no entiendo muy bien:

La especificación de lenguaje C # establece claramente lo siguiente en la sección 10.4:

El tipo especificado en una declaración constante debe ser sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, un tipo de enumeración o un tipo de referencia.

También establece en la sección 4.1.4 lo siguiente:

A través de las constaciones constantes es posible declarar constantes de los tipos simples (§10.4). No es posible tener constantes de otros tipos de estructura, pero los campos estáticos de solo lectura proporcionan un efecto similar.

Ok, entonces se puede obtener un efecto similar usando solo lectura estática . Leyendo esto fui y probé el siguiente código:

static void Main() { OffsetPoints(); Console.Write("Hit a key to exit..."); Console.ReadKey(); } static Point staticPoint = new Point(0, 0); static readonly Point staticReadOnlyPoint = new Point(0, 0); public static void OffsetPoints() { PrintOutPoints(); staticPoint.Offset(1, 1); staticReadOnlyPoint.Offset(1, 1); Console.WriteLine("Offsetting..."); Console.WriteLine(); PrintOutPoints(); } static void PrintOutPoints() { Console.WriteLine("Static Point: X={0};Y={1}", staticPoint.X, staticPoint.Y); Console.WriteLine("Static readonly Point: X={0};Y={1}", staticReadOnlyPoint.X, staticReadOnlyPoint.Y); Console.WriteLine(); }

La salida de este código es:

Punto estático: X = 0; Y = 0

Punto de solo lectura estático: X = 0; Y = 0

Compensando ...

Punto estático: X = 1; Y = 1

Punto de solo lectura estático: X = 0; Y = 0

Pulse una tecla para salir ...

Realmente esperaba que el compilador me diera algún tipo de advertencia sobre la mutación de un campo de solo lectura estático o en su defecto, para mutar el campo como lo haría con un tipo de referencia.

Sé que los tipos de valores mutables son malos (¿por qué Microsoft implementó Point como mutable es un misterio) pero no debería el compilador advertirle de alguna manera que está intentando mutar un tipo de valor de lectura estática ? ¿O al menos le advierte que su método de Offset() no tendrá los efectos secundarios "deseados"?


El comportamiento observado es una consecuencia desafortunada del hecho de que ni el Marco ni C # proporcionan ningún medio por el cual las declaraciones de funciones de los miembros puedan especificar si this debe pasarse por ref, const-ref o value. En su lugar, los tipos de valor siempre pasan this por ref (sin restricciones const) y los tipos de referencia siempre pasan this por valor.

El comportamiento ''correcto'' para un compilador sería prohibir el paso de valores inmutables o temporales por ref. No restringido const. Si se pudiera imponer dicha restricción, asegurar una semántica adecuada para tipos de valores mutables significaría seguir una regla simple: si hace una copia implícita de una estructura, está haciendo algo mal. Desafortunadamente, el hecho de que las funciones de los miembros solo puedan aceptar this mediante ref no restringidas por const, significa que un diseñador de idiomas debe elegir una de this tres opciones:

  1. Supongo que una función miembro no modificará `this`, y simplemente pasará variables inmutables o temporales por` ref`. Esto sería más eficiente para las funciones que, de hecho, no modifican "esto", pero podrían exponerse peligrosamente a modificaciones que deberían ser inmutables.
  2. No permita que las funciones miembro se utilicen en entidades inmutables o temporales. Esto evitaría una semántica inadecuada, pero sería una restricción realmente molesta, especialmente dado que la mayoría de las funciones de los miembros no modifican "esto".
  3. Permitir el uso de funciones miembro, excepto las que se considere más probable que modifiquen `this` (por ejemplo, establecedores de propiedades), pero en lugar de pasar entidades inmutables directamente por ref, cópielas en ubicaciones temporales y transfiéralas.

La elección de Microsoft protege las constantes de una modificación inadecuada, pero tiene las consecuencias desafortunadas de que el código se ejecute de forma innecesariamente lenta al llamar a funciones que no lo modifican, mientras que, en general, funciona incorrectamente para aquellas que lo hacen.

Dada la forma en que this se maneja, la mejor opción es evitar realizar cambios en las funciones de los miembros de la estructura que no sean los establecedores de propiedades. Tener establecedores de propiedades o campos mutables está bien, ya que el compilador prohibirá correctamente cualquier intento de usar establecedores de propiedades en objetos inmutables o temporales, o modificar cualquier campo de los mismos.


El efecto se debe a la combinación de varias características bien definidas.

readonly significa que el campo en cuestión no se puede cambiar, pero no que el destino del campo no se puede cambiar. Esto es más fácil de entender (y más a menudo es útil en la práctica) con campos de readonly de un tipo de referencia mutable, donde puede hacer x.SomeMutatingMethod() pero no x = someNewObject .

Entonces, el primer artículo es; puedes mutar el objetivo de un campo de readonly .

El segundo elemento es que, cuando accede a un tipo de valor no variable , obtiene una copia del valor. El ejemplo menos confuso de esto es giveMeAPoint().Offset(1, 1) porque no hay una ubicación conocida para que podamos observar más adelante que el tipo de valor devuelto por giveMeAPoint() puede o no haber sido mutado.

Esta es la razón por la cual los tipos de valor no son malos, pero son, de alguna manera, peores. El código verdaderamente malo no tiene un comportamiento bien definido, y todo esto está bien definido. Sin embargo, sigue siendo confuso (lo suficientemente confuso para que me equivoque en mi primera respuesta), y confundir es peor que el mal cuando intentas codificar. El mal fácilmente comprendido es mucho más fácil de evitar.


El punto de la sola lectura es que no puede reasignar la referencia o el valor.

En otras palabras, si intentaste esto

staticReadOnlyPoint = new Point(1, 1);

obtendrías un error del compilador porque estás intentando reasignar staticReadOnlyPoint . El compilador evitará que hagas esto.

Sin embargo, readonly no aplica si el valor o el objeto al que se hace referencia es mutable, es un comportamiento diseñado por la persona que lo crea en la clase o estructura.

[ EDITAR : para abordar adecuadamente el comportamiento extraño que se describe]

La razón por la que ve el comportamiento en donde staticReadOnlyPoint parece ser inmutable no es porque sea inmutable en sí misma, sino porque es una estructura de solo lectura. Esto significa que cada vez que accedes a él, estás tomando una copia completa.

Entonces tu linea

staticReadOnlyPoint.Offset(1, 1);

está accediendo, y mutando, una copia del campo, no el valor real en el campo. Cuando posteriormente escribe el valor, está escribiendo otra copia del original (no la copia mutada).

La copia que mutó con la llamada a Offset se descarta, porque nunca se asigna a nada.


Eric Lippert explica lo que está pasando here :

... si el campo es de solo lectura y la referencia se produce fuera de un constructor de instancia de la clase en la que se declara el campo, entonces el resultado es un valor, es decir, el valor del campo I en el objeto al que hace referencia E.

La palabra importante aquí es que el resultado es el valor del campo, no la variable asociada con el campo. Los campos de solo lectura no son variables fuera del constructor. (Se considera que el inicializador aquí está dentro del constructor; vea mi publicación anterior sobre ese tema).

Ah, y solo para enfatizar en la maldad de las estructuras mutables, aquí está su conclusión:

Esta es otra razón por la cual los tipos de valores mutables son malos. Intenta siempre hacer tipos de valor inmutables.


Si observa el IL, verá que al usar el campo de readonly , se hace una copia antes de llamar a Offset :

IL_0014: ldsfld valuetype [System.Drawing]System.Drawing.Point Program::staticReadOnlyPoint IL_0019: stloc.0 IL_001a: ldloca.s CS$0$0000

Por qué esto está pasando, está más allá de mí.

Podría ser parte de la especificación, o un error del compilador (pero parece un poco demasiado intencional para el último).