c# struct immutability

c# - ¿Funciona el uso de campos públicos de solo lectura para estructuras inmutables?



immutability (3)

¿Es esta una forma adecuada de declarar estructuras inmutables?

public struct Pair { public readonly int x; public readonly int y; // Constructor and stuff }

No puedo pensar por qué esto podría tener problemas, pero solo quería preguntar para asegurarme.

En este ejemplo, utilicé ints. ¿Qué sucede si utilizo una clase en su lugar, pero esa clase también es inmutable, como tal? Eso debería funcionar bien también, ¿no?

public struct Pair { public readonly (immutableClass) x; public readonly (immutableClass) y; // Constructor and stuff }

(Aparte: entiendo que el uso de propiedades es más generalizable y permite cambios, pero esta estructura está destinada literalmente a almacenar solo dos valores. Me interesa la cuestión de la inmutabilidad aquí).


El compilador prohibirá la asignación a los campos de readonly , así como a las propiedades de solo lectura.

Recomiendo usar propiedades de solo lectura principalmente por razones de interfaz pública y enlace de datos (que no funcionará en los campos). Si fuera mi proyecto, requeriría eso si la estructura / clase es pública. Si va a ser interno a un ensamblado o privado a una clase, podría pasarlo por alto al principio y refactorizarlos para propiedades de solo lectura más adelante.


Eso lo haría inmutable de hecho. Supongo que será mejor que añadas un constructor.
Si todos sus miembros son inmutables también, esto lo haría completamente inmutable. Estos pueden ser clases o valores simples.


Si vas a usar estructuras, es una buena práctica hacerlas inmutables.

Hacer que todos los campos sean de solo lectura es una excelente manera de ayudar a (1) documentar que la estructura es inmutable y (2) prevenir mutaciones accidentales.

Sin embargo, hay una arruga, que en realidad en una extraña coincidencia que estaba planeando bloguear sobre la próxima semana. Es decir: solo en un campo de estructura es una mentira . Uno espera que un campo de solo lectura no pueda cambiar, pero por supuesto que sí. "readonly" en un campo struct es la declaración que escribe cheques sin dinero en su cuenta. Una estructura no posee su almacenamiento, y es ese almacenamiento el que puede mutar.

Por ejemplo, tomemos su estructura:

public struct Pair { public readonly int x; public readonly int y; public Pair(int x, int y) { this.x = x; this.y = y; } public void M(ref Pair p) { int oldX = x; int oldY = y; // Something happens here Debug.Assert(x == oldX); Debug.Assert(y == oldY); } }

¿Hay algo que pueda suceder en "algo sucede aquí" que causa que se violen las aserciones de depuración? Por supuesto.

public void M(ref Pair p) { int oldX = this.x; int oldY = this.y; p = new Pair(0, 0); Debug.Assert(this.x == oldX); Debug.Assert(this.y == oldY); } ... Pair myPair = new Pair(10, 20); myPair.M(ref myPair);

Y ahora que pasa? ¡La afirmación es violada! "this" y "p" se refieren a la misma ubicación de almacenamiento. La ubicación de almacenamiento está mutada, por lo que los contenidos de "esto" están mutados porque son la misma cosa. La estructura no es capaz de aplicar la función de solo lectura de xey debido a que la estructura no es propietaria del almacenamiento; el almacenamiento es una variable local que puede mutar libremente tanto como lo desee.

No puede confiar en la invariante de que nunca se observa que un campo de solo lectura en una estructura cambie; Lo único en lo que puede confiar es que no puede escribir código que lo cambie directamente. Pero con un pequeño trabajo furtivo como este, puedes cambiarlo indirectamente, todo lo que quieras.

Ver también el excelente artículo de blog de Joe Duffy sobre este tema:

http://joeduffyblog.com/2010/07/01/when-is-a-readonly-field-not-readonly/