Pregunta

Leí todas las preguntas relacionadas con este tema y todas dan razones por las cuales un constructor predeterminado en un struct no está disponible en C#, pero todavía no he encontrado a nadie que sugiera un curso de acción general ante esta situación.

La solución obvia es simplemente convertir el struct a un class y afrontar las consecuencias.

¿Existen otras opciones para conservarlo como struct?

Me encontré con esta situación con uno de nuestros objetos API de comercio interno.El diseñador lo convirtió de un class a un struct, y ahora el constructor predeterminado (que antes era privado) deja el objeto en un estado no válido.

Pensé que si vamos a mantener el objeto como struct, debería introducirse un mecanismo para comprobar la validez del Estado (algo así como un IsValid propiedad).Me encontré con mucha resistencia y una explicación de "quien use la API no debería usar el constructor predeterminado", un comentario que ciertamente me sorprendió.(Nota:el objeto en cuestión se construye "correctamente" a través de métodos de fábrica estáticos, y todos los demás constructores son internal.)

¿Está todo el mundo simplemente convirtiendo su structs a class¿Estás en esta situación sin pensarlo dos veces?

Editar:Me gustaría ver algunas sugerencias sobre cómo mantener este tipo de objetos como struct -- el objeto en cuestión arriba es mucho más adecuado como struct que como un class.

¿Fue útil?

Solución

Para un struct, diseña el tipo de modo que la instancia construida predeterminada (campos todos cero) sea un estado válido. No [ no deberá ] usar arbitrariamente class en lugar de <=> sin una buena razón: no hay nada de malo en usar un tipo de referencia inmutable.

Mis sugerencias:

  • Asegúrese de que la razón para usar un <=> sea válida (un perfilador [real] reveló problemas de rendimiento significativos como resultado de la asignación pesada de un objeto muy liviano).
  • Diseñe el tipo para que la instancia construida predeterminada sea válida.
  • Si el diseño del tipo está dictado por restricciones de interoperabilidad nativas / COM, ajuste la funcionalidad y no exponga el <=> fuera del contenedor (tipo anidado privado). De esa manera, puede documentar y verificar fácilmente el uso adecuado de los requisitos de tipo restringido.

Otros consejos

La razón de esto es que una estructura (una instancia de System.ValueType) es tratada especialmente por el CLR: se inicializa con todos los campos en 0 (o predeterminado). Realmente ni siquiera necesita crear uno, simplemente declararlo. Es por eso que se requieren constructores predeterminados.

Puede solucionar esto de dos maneras:

  1. Cree una propiedad como IsValid para indicar si es una estructura válida, como usted indica y
  2. en .Net 2.0 considere usar Nullable < T > para permitir una estructura no inicializada (nula).

Cambiar la estructura a una clase puede tener algunas consecuencias muy sutiles (en términos de uso de memoria e identidad de objeto que surgen más en un entorno multiproceso), y NullReferenceExceptions no tan sutiles pero difíciles de depurar para objetos no inicializados.

La razón por la cual no hay posibilidad de definir un constructor predeterminado se ilustra con la siguiente expresión:

new MyStruct[1000];

Tienes 3 opciones aquí

  1. llamando al constructor predeterminado 1000 veces, o
  2. crear datos corruptos (tenga en cuenta que una estructura puede contener referencias; si no inicializa o deja en blanco la referencia, podría acceder a memoria arbitraria), o
  3. vacía la memoria asignada con ceros (en el nivel de bytes).

.NET hace lo mismo tanto para las estructuras como para las clases: los campos y los elementos de la matriz están en blanco con ceros. Esto también consigue un comportamiento más consistente entre estructuras y clases y ningún código inseguro. También permite que .NET Framework no se especialice en algo como new byte[1000].

Y ese es el constructor predeterminado para las estructuras .NET exige y se cuida solo: cero todos los bytes.

Ahora, para manejar esto, tienes un par de opciones:

  • Agregue una propiedad Am-I-Initialized a la estructura (como HasValue en Nullable).
  • Permitir que la estructura puesta a cero sea un valor válido (como 0 es un valor válido para un decimal).
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top