Una estructura es, en esencia, nada más y nada 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á como un objeto . Una variable que contiene una referencia a dicho objeto de montón (estructura "en recuadro") 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 struct-versus-class 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 directrices de MS no distinguen entre ellas. Algunas veces hay una 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 parecido a 24-32. A veces, sin embargo, lo que se necesita es una agregación de variables. Una estructura utilizada para ese fin debería consistir simplemente en un grupo de campos públicos, y posiblemente una anulación Equals
, una anulación ToString
y una implementación IEquatable(itsType).Equals
. Las estructuras que se usan como agregaciones de campos no son objetos ni deberían pretender serlo. Desde el punto de vista de la estructura, el significado del campo debe ser nada más o menos que "lo último escrito en este campo". Cualquier significado adicional debe ser determinado por el código del cliente. Por ejemplo, si una estructura agregante de variables tiene miembros Minimum
y Maximum
, la estructura en sí no debe prometer que Minimum <= Maximum
. El código que recibe dicha estructura como parámetro debe comportarse como si se pasaran valores Minimum
y Maximum
por separado. Un requisito de que Minimum
no sea mayor que Maximum
debe considerarse como un requisito de que un parámetro Minimum
no sea mayor que uno Maximum
por separado.
Un modelo útil tener en cuenta a veces es tener una ExposedHolder<T>
clase definida algo como:
class ExposedHolder<T>
{
public T Value;
ExposedHolder() { }
ExposedHolder(T val) { Value = T; }
}
Si uno tiene un List<ExposedHolder<someStruct>>
, donde someStruct
es una estructura variable de agregación, uno puede hacer cosas como myList[3].Value.someField += 7;
, pero dando myList[3].Value
a otro código le dará el contenido de Value
en lugar de darle la posibilidad de alterarlo. Por el contrario, si uno usara un List<someStruct>
, sería necesario usar var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Si se utilizara un tipo de clase mutable, exponer el contenido de myList[3]
al código externo requeriría copiar todos los campos a algún otro objeto. Si se 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 por someField
, que 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 conveniente 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. Las matrices de estructuras son especiales, en esa indexación se obtendrá una referencia directa a una estructura interna, por lo que se puede decir "a [12] .x = 5;". Aunque se pueden definir objetos similares a una matriz, C# no les permite compartir dicha sintaxis con las matrices.
¿Van a tener campos públicos o también van a tener métodos? ¿Son los tipos tipos primitivos, como los enteros? ¿Se incluirán en una matriz, o en algo como List? –
JeffFerguson
En caso de duda use una clase. Si necesita la inicialización automática en una matriz, use una estructura. – leppie
¿Una lista de estructuras mutables? Cuidado con el velociraptor. –