Una estructura es, en el fondo, nada más ni menos que una agregación de campos. En .NET es posible que una estructura "pretenda" ser un objeto, y para cada tipo de estructura .NET define implícitamente un tipo de objeto de montón con los mismos campos y métodos que, al ser un objeto de montón, se comportarán como un objeto. . Una variable que contiene una referencia a dicho objeto de montón (estructura "en caja") exhibirá semántica de referencia, pero una que contiene una estructura directamente es simplemente una agregación de variables.
Creo que gran parte de la confusión estructura versus clase se debe al hecho de que las estructuras tienen dos casos de uso muy diferentes, que deberían tener pautas de diseño muy diferentes, pero las pautas de MS no distinguen entre ellas. A veces existe la necesidad de algo que se comporte como un objeto; en ese caso, las pautas de MS son bastante razonables, aunque el "límite de 16 bytes" probablemente debería ser más como 24-32. A veces, sin embargo, lo que se necesita es una agregación de variables. Una estructura utilizada para ese propósito debería consistir simplemente en un montón de campos públicos, y posiblemente una Equals
anulación,ToString
anulación yIEquatable(itsType).Equals
implementación. Las estructuras que se utilizan como agregaciones de campos no son objetos y no deberían pretender serlo. Desde el punto de vista de la estructura, el significado de campo debe ser ni más ni menos que "lo último que se escribe en este campo". Cualquier significado adicional debe ser determinado por el código del cliente.
Por ejemplo, si una estructura de agregación de variables tiene miembros Minimum
y Maximum
, la estructura en sí no debería prometer eso Minimum <= Maximum
. El código que recibe dicha estructura como parámetro debe comportarse como si se le pasaran valores Minimum
y separados Maximum
. Un requisito que Minimum
no sea mayor que Maximum
debe considerarse como un requisito de que un Minimum
parámetro no sea mayor que Maximum
uno pasado por separado .
Un patrón útil a considerar a veces es tener una ExposedHolder<T>
clase definida como:
class ExposedHolder<T>
{
public T Value;
ExposedHolder() { }
ExposedHolder(T val) { Value = T; }
}
Si uno tiene List<ExposedHolder<someStruct>>
, where someStruct
es una estructura de agregación de variables, uno puede hacer cosas como myList[3].Value.someField += 7;
, pero darle myList[3].Value
a otro código le dará el contenido en Value
lugar de darle un medio para alterarlo. Por el contrario, si se usara List<someStruct>
, sería necesario usar var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Si uno usa un tipo de clase mutable, exponer el contenido de myList[3]
un código externo requeriría copiar todos los campos a algún otro objeto. Si uno usara un tipo de clase inmutable, o una estructura de "estilo de objeto", sería necesario construir una nueva instancia que fuera como myList[3]
excepto someField
cuál era diferente, y luego almacenar esa nueva instancia en la lista.
Una nota adicional: si está almacenando una gran cantidad de cosas similares, puede ser bueno almacenarlas en matrices de estructuras posiblemente anidadas, preferiblemente tratando de mantener el tamaño de cada matriz entre 1K y 64K más o menos. Los arreglos de estructuras son especiales, en el sentido de que indexar uno producirá una referencia directa a una estructura interna, por lo que se puede decir "a [12] .x = 5;". Aunque se pueden definir objetos similares a matrices, C # no les permite compartir dicha sintaxis con matrices.