Eine Struktur ist im Kern nichts anderes als eine Ansammlung von Feldern. In .NET kann eine Struktur "vorgeben", ein Objekt zu sein, und für jeden Strukturtyp definiert .NET implizit einen Heap-Objekttyp mit denselben Feldern und Methoden, die sich als Heap-Objekt wie ein Objekt verhalten . Eine Variable, die einen Verweis auf ein solches Heap-Objekt enthält ("Boxed" -Struktur), weist eine Referenzsemantik auf, aber eine Variable, die eine Struktur direkt enthält, ist einfach eine Aggregation von Variablen.
Ich denke, ein Großteil der Verwirrung zwischen Struktur und Klasse beruht auf der Tatsache, dass Strukturen zwei sehr unterschiedliche Anwendungsfälle haben, die sehr unterschiedliche Entwurfsrichtlinien haben sollten, aber die MS-Richtlinien unterscheiden nicht zwischen ihnen. Manchmal besteht Bedarf an etwas, das sich wie ein Objekt verhält. In diesem Fall sind die MS-Richtlinien ziemlich vernünftig, obwohl das "16-Byte-Limit" wahrscheinlich eher 24-32 betragen sollte. Manchmal ist jedoch eine Aggregation von Variablen erforderlich. Eine zu diesem Zweck verwendete Struktur sollte einfach aus einer Reihe öffentlicher Felder und möglicherweise aus einem Equals
Override, ToString
Override und bestehenIEquatable(itsType).Equals
Implementierung. Strukturen, die als Aggregationen von Feldern verwendet werden, sind keine Objekte und sollten dies auch nicht vorgeben. Aus Sicht der Struktur sollte die Bedeutung des Feldes nicht mehr oder weniger sein als "das Letzte, was in dieses Feld geschrieben wurde". Jede zusätzliche Bedeutung sollte durch den Client-Code bestimmt werden.
Wenn beispielsweise eine variabel aggregierende Struktur Mitglieder hat Minimum
und Maximum
, sollte die Struktur selbst dies nicht versprechen Minimum <= Maximum
. Code, der eine solche Struktur als Parameter empfängt, sollte sich so verhalten, als ob er separat Minimum
und mit Maximum
Werten übergeben würde. Eine Anforderung, Minimum
die nicht größer als Maximum
ist, sollte als Anforderung angesehen werden, dass ein Minimum
Parameter nicht größer als ein separat übergebener Parameter sein darf Maximum
.
Ein nützliches Muster, das manchmal berücksichtigt werden muss, ist die ExposedHolder<T>
Definition einer Klasse wie:
class ExposedHolder<T>
{
public T Value;
ExposedHolder() { }
ExposedHolder(T val) { Value = T; }
}
Wenn man eine Struktur hat List<ExposedHolder<someStruct>>
, bei der someStruct
es sich um eine variabel aggregierende Struktur handelt, kann man Dinge wie tun myList[3].Value.someField += 7;
, aber wenn man myList[3].Value
anderen Code gibt, erhält man den Inhalt von, Value
anstatt ihm ein Mittel zu geben, ihn zu ändern. Im Gegensatz List<someStruct>
dazu wäre es notwendig , wenn man a verwendet , es zu verwenden var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Wenn ein veränderbarer Klassentyp verwendet wird, myList[3]
müssen alle Felder in ein anderes Objekt kopiert werden , um den Inhalt für externen Code verfügbar zu machen. Wenn man einen unveränderlichen Klassentyp oder eine Struktur im "Objektstil" verwendet, muss man eine neue Instanz erstellen, die ähnlich ist, myList[3]
außer dass sie someField
anders ist, und diese neue Instanz dann in der Liste speichern.
Ein zusätzlicher Hinweis: Wenn Sie eine große Anzahl ähnlicher Dinge speichern, kann es sinnvoll sein, sie in möglicherweise verschachtelten Arrays von Strukturen zu speichern, wobei Sie vorzugsweise versuchen, die Größe jedes Arrays zwischen 1 KB und 64 KB oder so zu halten. Arrays von Strukturen sind insofern besonders, als die Indizierung einen direkten Verweis auf eine Struktur innerhalb ergibt, so dass man "a [12] .x = 5;" sagen kann. Obwohl man Array-ähnliche Objekte definieren kann, erlaubt C # ihnen nicht, diese Syntax mit Arrays zu teilen.