Inmutabilidad de las estructuras

Posible duplicado:
¿Por qué las estructuras mutables son malvadas?

Lo leí en muchos lugares, incluido aquí, que es mejor hacer que las estructuras sean inmutables.

¿Cuál es la razón detrás de esto? Veo muchas estructuras creadas por Microsoft que son mutables, como las de xna. Probablemente hay muchos más en el BCL.

¿Cuáles son los pros y los contras de no seguir esta guía?

Las estructuras deben representar valores . Los valores no cambian El número 12 es eterno.

Sin embargo, considere:

Foo foo = new Foo(); // a mutable struct foo.Bar = 27; Foo foo2 = foo; foo2.Bar = 55; 

Ahora foo.Bar y foo2.Bar son diferentes, lo que a menudo es inesperado. Especialmente en los escenarios como propiedades (afortunadamente el comstackdor lo detecta). Pero también colecciones, etc .; ¿Cómo puedes mutarlos sensatamente?

La pérdida de datos es demasiado fácil con estructuras mutables.

La gran estafa es que las cosas no se comportan como esperabas, especialmente si la mutabilidad proviene de una mezcla del valor directo y un tipo de referencia dentro de ella.

Para ser sincero, no puedo recordar todos los problemas extraños que he visto surgir en grupos de noticias cuando han utilizado estructuras mutables, pero esas razones ciertamente existen. Las estructuras mutables causan problemas. Mantente alejado.

EDITAR: Acabo de encontrar un correo electrónico que escribí hace un tiempo sobre este tema. Se elabora un poco:

  • Es filosóficamente erróneo: una estructura debería representar algún tipo de valor fundamental. Esos son básicamente inmutables. No puede cambiar el número 5. Puede cambiar el valor de una variable de 5 a 6, pero lógicamente no realiza un cambio en el valor en sí.

  • Es prácticamente un problema: crea muchas situaciones extrañas. Es particularmente malo si es mutable a través de una interfaz. Luego puede comenzar a cambiar los valores encuadrados. Ick. He visto muchas publicaciones de grupos de noticias que se deben a personas que intentan usar estructuras mutables y problemas. Vi un ejemplo LINQ muy extraño que estaba fallando porque List.Enumerator es una estructura, por ejemplo.

Utilizo estructuras mutables a menudo en mi proyecto (crítico para el rendimiento) y no tengo problemas porque entiendo las implicaciones de la semántica de copia. Hasta donde puedo decir, la razón principal por la que las personas defienden las estructuras inmutables es para que las personas que no entienden las implicaciones no puedan meterse en problemas.

Eso no es algo tan terrible, pero ahora corremos peligro de que se convierta en “la verdad del Evangelio”, cuando de hecho hay momentos en los que es legítimamente la mejor opción para hacer una estructura mutable. Al igual que con todas las cosas, hay excepciones a la regla.

No hay nada más barato de manipular que una estructura mutable, por lo que a menudo lo ves en un código de alto rendimiento como las rutinas de procesamiento de gráficos.

Desafortunadamente las estructuras mutables no funcionan bien con los objetos y las propiedades, es demasiado fácil modificar una copia de un stuct en lugar de la estructura en sí. Por lo tanto, no son apropiados para la mayoría de tu código.

PD Para evitar el costo de copiar estructuras mutables, generalmente se almacenan y pasan en matrices.

La razón técnica es que las estructuras mutables parecen ser capaces de hacer cosas que realmente no hacen. Dado que la semántica de tiempo de diseño es la misma que la de los tipos de referencia, esto resulta confuso para los desarrolladores. Este código:

 public void DoSomething(MySomething something) { something.Property = 10; } 

Se comporta de manera bastante diferente dependiendo de si MySomething es una struct o una class . Para mí, esta es una razón convincente, pero no la más convincente. Si observa el Objeto de valor de DDD , puede ver la conexión a cómo deben tratarse las estructuras. Un objeto de valor en DDD se puede representar mejor como un tipo de valor en .Net (y, por lo tanto, una estructura). Debido a que no tiene identidad, no puede cambiar.

Piense en esto en términos de algo así como su dirección. Puede “cambiar” su dirección, pero la dirección en sí misma no ha cambiado. De hecho, tiene una nueva dirección asignada a usted. Conceptualmente, esto funciona, porque si realmente cambiaste tu dirección, tus compañeros también tendrían que mudarse.

Usted ha pedido los pros y los contras de no seguir las pautas que las estructuras deben ser inmutables.

Contras: Los contras están bien cubiertos en las respuestas existentes, y la mayoría de los problemas descritos se deben a la misma causa: comportamiento inesperado debido a la semántica de valores de las estructuras .

Pros: El principal experto en el uso de estructuras mutables puede ser el rendimiento . Obviamente, este consejo viene con todas las advertencias habituales sobre optimizaciones: asegúrese de que parte de su código se debe optimizar y asegúrese de que cualquier cambio optimice realmente el rendimiento de su código a través del perfil.

Para ver un artículo excelente sobre cuándo podría querer utilizar estructuras mutables, consulte el cuestionario de rendimiento de Rico Mariani sobre progtwigción basada en valores (o más específicamente, las respuestas ).

Una estructura generalmente debe representar una unidad única de algún tipo. Como tal, no tiene mucho sentido cambiar una de las propiedades del valor, tiene más sentido crear un valor completamente nuevo si desea un valor que sea diferente de alguna manera.

La semántica se simplifica al usar estructuras inmutables, y evita las trampas como esta:

 // a struct struct Interval { int From { get; set; } int To { get; set; } } // create a list of structs List intervals = new List(); // add a struct to the list intervals.Add(new Interval()); // try to set the values of the struct intervals[0].From = 10; intervals[0].To = 20; 

El resultado es que la estructura en la lista no se cambia en absoluto. La expresión Interval [0] copia el valor de la estructura de la lista, luego cambia la propiedad del valor temporal, pero el valor nunca se vuelve a poner en la lista.

Editar: se cambió el ejemplo para usar una lista en lugar de una matriz.

Cuando copie las estructuras, copie sus contenidos, de modo que si modifica una versión copiada, el “original” no se actualizará.

Esta es una fuente de errores, ya que incluso si sabes que caes en la trampa de copiar una estructura (simplemente pasándola a un método) y modificar la copia.

Me acabo de pasar la semana pasada, me mantuvo una hora buscando un error.

Mantener las estructuras inmutables evita que …

Además de eso, debes asegurarte de que tienes un buen motivo para usar las estructuras en primer lugar: “optimización” o “quiero algo que se asigne rápidamente en la stack” no cuenta como respuesta. Marshaling o cosas en las que dependes del diseño, vale, pero normalmente no deberías mantener esas estructuras por mucho tiempo, son valores, no objetos.

La razón por la que debe hacer que las estructuras sean inmutables es que son ValueTypes , lo que significa que se copian cada vez que las transfiere a un método.

Entonces, si, por ejemplo, tuvieras una propiedad que devolviera una estructura, modificar el valor de un campo en esa estructura sería inútil, porque el getter devolvería una copia de la estructura, en lugar de una referencia a la estructura. He visto esto hecho en código, y a menudo es difícil de atrapar.

Si diseñas tus estructuras de inmutabilidad, ayudas al progtwigdor a evitar estos errores.