Una struttura è, in fondo, niente di più né di meno che un'aggregazione di campi. In .NET è possibile che una struttura "finga" di essere un oggetto, e per ogni tipo di struttura .NET definisce implicitamente un tipo di oggetto heap con gli stessi campi e metodi che, essendo un oggetto heap, si comporteranno come un oggetto . Una variabile che contiene un riferimento a tale oggetto heap (struttura "boxed") mostrerà la semantica di riferimento, ma quella che contiene direttamente una struttura è semplicemente un'aggregazione di variabili.
Penso che gran parte della confusione tra struttura e classe derivi dal fatto che le strutture hanno due casi di utilizzo molto diversi, che dovrebbero avere linee guida di progettazione molto diverse, ma le linee guida MS non fanno distinzione tra di loro. A volte c'è bisogno di qualcosa che si comporti come un oggetto; in tal caso, le linee guida MS sono abbastanza ragionevoli, sebbene il "limite di 16 byte" dovrebbe probabilmente essere più simile a 24-32. A volte, tuttavia, ciò che serve è un'aggregazione di variabili. Una struttura usata a tale scopo dovrebbe semplicemente consistere in un gruppo di campi pubblici e possibilmente un Equals
override, ToString
override eIEquatable(itsType).Equals
implementazione. Le strutture usate come aggregazioni di campi non sono oggetti e non dovrebbero fingere di esserlo. Dal punto di vista della struttura, il significato di campo non dovrebbe essere né più né meno che "l'ultima cosa scritta in questo campo". Qualsiasi significato aggiuntivo dovrebbe essere determinato dal codice client.
Ad esempio, se una struttura di aggregazione di variabili ha membri Minimum
e Maximum
, la struttura stessa non dovrebbe prometterlo Minimum <= Maximum
. Il codice che riceve una tale struttura come parametro dovrebbe comportarsi come se fosse passato separatamente Minimum
e Maximum
valori. Un requisito che Minimum
non sia maggiore di Maximum
dovrebbe essere considerato come un requisito che un Minimum
parametro non sia maggiore di uno passato separatamente Maximum
.
Un modello utile da considerare a volte è avere una ExposedHolder<T>
classe definita qualcosa come:
class ExposedHolder<T>
{
public T Value;
ExposedHolder() { }
ExposedHolder(T val) { Value = T; }
}
Se uno ha una List<ExposedHolder<someStruct>>
, in cui someStruct
è una struttura variabile aggregazione, uno può fare le cose come myList[3].Value.someField += 7;
, ma dando myList[3].Value
ad altro codice darà il contenuto di Value
piuttosto che dare è un mezzo di alterarlo. Al contrario, se si usasse a List<someStruct>
, sarebbe necessario usare var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Se si utilizza un tipo di classe mutabile, l'esposizione del contenuto di myList[3]
al codice esterno richiederebbe la copia di tutti i campi su qualche altro oggetto. Se si usasse un tipo di classe immutabile, o una struttura "in stile oggetto", sarebbe necessario costruire una nuova istanza che fosse simile, myList[3]
tranne quella someField
diversa, e quindi memorizzare quella nuova istanza nell'elenco.
Una nota aggiuntiva: se si memorizza un gran numero di cose simili, potrebbe essere utile memorizzarle in matrici di strutture possibilmente annidate, preferibilmente cercando di mantenere la dimensione di ogni matrice tra 1K e 64K circa. Le matrici di strutture sono speciali, in quanto l'indicizzazione produrrà un riferimento diretto a una struttura all'interno, quindi si può dire "a [12] .x = 5;". Sebbene sia possibile definire oggetti simili ad array, C # non consente loro di condividere tale sintassi con gli array.