La regla general a seguir es que las estructuras deben ser colecciones pequeñas, simples (de un nivel) de propiedades relacionadas, que son inmutables una vez creadas; para cualquier otra cosa, use una clase.
C # es bueno en que las estructuras y las clases no tienen diferencias explícitas en la declaración aparte de la palabra clave que define; Por lo tanto, si siente que necesita "actualizar" una estructura a una clase o, a la inversa, "degradar" una clase a una estructura, es principalmente una simple cuestión de cambiar la palabra clave (hay algunos otros problemas; las estructuras no pueden derivarse de cualquier otra clase o tipo de estructura, y no pueden definir explícitamente un constructor sin parámetros predeterminado).
Digo "en su mayoría", porque lo más importante que se debe saber acerca de las estructuras es que, debido a que son tipos de valor, tratarlas como clases (tipos de referencia) puede terminar con un dolor y medio. Particularmente, hacer que las propiedades de una estructura sean mutables puede causar un comportamiento inesperado.
Por ejemplo, supongamos que tiene una clase SimpleClass con dos propiedades, A y B. Usted crea una copia de esta clase, inicializa A y B, y luego pasa la instancia a otro método. Ese método modifica aún más A y B. De nuevo en la función de llamada (la que creó la instancia), A y B de su instancia tendrán los valores que les da el método llamado.
Ahora, lo haces una estructura. Las propiedades siguen siendo mutables. Realiza las mismas operaciones con la misma sintaxis que antes, pero ahora, los nuevos valores de A y B no están en la instancia después de llamar al método. ¿Que pasó? Bueno, tu clase ahora es una estructura, lo que significa que es un tipo de valor. Si pasa un tipo de valor a un método, el valor predeterminado (sin una palabra clave out o ref) es pasar "por valor"; se crea una copia superficial de la instancia para que la utilice el método, y luego se destruye cuando el método se realiza dejando intacta la instancia inicial.
Esto se vuelve aún más confuso si tuviera un tipo de referencia como miembro de su estructura (no rechazada, pero extremadamente mala práctica en prácticamente todos los casos); la clase no se clonaría (solo la referencia de la estructura a ella), por lo que los cambios en la estructura no afectarán al objeto original, pero los cambios en la subclase de la estructura Afectarán la instancia desde el código que llama. Esto puede fácilmente poner estructuras mutables en estados muy inconsistentes que pueden causar errores muy lejos de donde está el problema real.
Por esta razón, prácticamente todas las autoridades de C # dicen que siempre deben hacer que sus estructuras sean inmutables; permitir al consumidor especificar los valores de las propiedades solo en la construcción de un objeto, y nunca proporcionar ningún medio para cambiar los valores de esa instancia. Los campos de solo lectura, o propiedades de solo obtención, son la regla. Si el consumidor desea cambiar el valor, puede crear un nuevo objeto basado en los valores del anterior, con los cambios que desee, o puede llamar a un método que hará lo mismo. Esto les obliga a tratar una sola instancia de su estructura como un "valor" conceptual, indivisible y distinto de (pero posiblemente equiparable a) todos los demás. Si realizan una operación en un "valor" almacenado por su tipo, obtendrán un nuevo "valor" que es diferente de su valor inicial, pero aún así comparable y / o semánticamente compatible.
Para un buen ejemplo, mira el tipo de DateTime. No puede asignar ninguno de los campos de una instancia de DateTime directamente; debe crear uno nuevo o llamar a un método en el existente que producirá una nueva instancia. Esto se debe a que la fecha y la hora son un "valor", como el número 5, y un cambio al número 5 da como resultado un nuevo valor que no es 5. Solo porque 5 + 1 = 6 no significa que 5 sea ahora 6 porque le has añadido 1. DateTimes funciona de la misma manera; 12:00 no se "convierte" en 12:01 si agrega un minuto, en lugar de eso, obtiene un nuevo valor 12:01 que es distinto de 12:00. Si esta es una situación lógica para su tipo (los buenos ejemplos conceptuales que no están integrados en .NET son dinero, distancia, peso y otras cantidades de una UOM donde las operaciones deben tener en cuenta todas las partes del valor), A continuación, utilice una estructura y diseñe en consecuencia. En la mayoría de los otros casos en los que los subelementos de un objeto deben ser mutables independientemente, use una clase.