Die von OP referenzierte Quelle hat eine gewisse Glaubwürdigkeit ... aber wie steht es mit Microsoft - was ist die Meinung zur Verwendung von Strukturen? Ich habe mir einige zusätzliche Informationen von Microsoft gesucht, und hier ist, was ich gefunden habe:
Erwägen Sie das Definieren einer Struktur anstelle einer Klasse, wenn Instanzen des Typs klein und häufig kurzlebig sind oder häufig in anderen Objekten eingebettet sind.
Definieren Sie keine Struktur, es sei denn, der Typ hat alle folgenden Merkmale:
- Er stellt logisch einen einzigen Wert dar, ähnlich wie primitive Typen (Integer, Double usw.).
- Die Instanzgröße ist kleiner als 16 Bytes.
- Es ist unveränderlich.
- Es muss nicht häufig geboxt werden.
Microsoft verletzt diese Regeln konsequent
Okay, zumindest #2 und #3. Unser geliebtes Wörterbuch hat 2 interne Strukturen:
[StructLayout(LayoutKind.Sequential)] // Standard für Strukturen
private struct Eintrag //
{
// Code anzeigen auf *Referenzquelle
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Enumerator :
IEnumerator>, IDisposable,
IDictionaryEnumerator, IEnumerator
{
// Code anzeigen auf *Referenzquelle
}
*Referenzquelle
Die Quelle 'JonnyCantCode.com' hat 3 von 4 - ziemlich verzeihlich, da #4 wahrscheinlich kein Problem wäre. Wenn Sie sich dabei erwischen, wie Sie eine Struktur boxen, überdenken Sie Ihre Architektur.
Schauen wir uns an, warum Microsoft diese Strukturen verwenden würde:
- Jede Struktur,
Eintrag
und Enumerator
, repräsentiert einzelne Werte.
- Geschwindigkeit
Eintrag
wird nie als Parameter außerhalb der Dictionary-Klasse übergeben. Weitere Untersuchungen zeigen, dass Dictionary zur Erfüllung der Implementierung von IEnumerable die Enumerator
-Struktur verwendet, die jedes Mal kopiert wird, wenn ein Enumerator angefordert wird ... macht Sinn.
- Intern zur Dictionary-Klasse.
Enumerator
ist öffentlich, weil Dictionary auflistbar ist und eine gleichwertige Zugänglichkeit zur IEnumerator-Schnittstellenimplementierung haben muss - z.B. IEnumerator-Getter.
Aktualisierung - Darüber hinaus erkennen Sie, dass eine Struktur, wenn Enumerator eine Schnittstelle implementiert, und als diese implementierte Typus umgewandelt wird, die Struktur zu einem Referenztypus wird und auf den Heap verschoben wird. Intern zur Dictionary-Klasse ist Enumerator jedoch immer noch ein Werttypus. Sobald jedoch eine Methode GetEnumerator()
aufruft, wird ein Referenztypus IEnumerator
zurückgegeben.
Was wir hier nicht sehen, ist ein Versuch oder ein Beweis für die Notwendigkeit, Strukturen unveränderlich zu halten oder eine Instanzgröße von nur 16 Bytes oder weniger beizubehalten:
- In den obigen Strukturen ist nichts als
schreibgeschützt
deklariert - nicht unveränderlich
- Die Größe dieser Struktur könnte weit über 16 Bytes liegen
Eintrag
hat eine unbestimmte Lebensdauer (von Hinzufügen()
über Entfernen()
, Leeren()
oder Garbage Collection);
Und ... 4. Beide Strukturen speichern TKey und TValue, von denen wir alle wissen, dass sie durchaus in der Lage sind, Referenztypen zu sein (zusätzliche Bonusinformationen)
Ungeachtet der gehashten Schlüssel sind Wörterbücher schnell, unter anderem weil die Instanziierung einer Struktur schneller ist als die eines Referenztypus. Hier habe ich ein Dictionary
, welches 300.000 zufällige Ganzzahlen mit sequenziell inkrementierten Schlüsseln speichert.
Kapazität: 312874
MemGröße: 2660827 Bytes
Abgeschlossene Größenänderung: 5ms
Gesamtzeit zum Füllen: 889ms
Kapazität: Anzahl der Elemente, die verfügbar sind, bevor das interne Array vergrößert werden muss.
MemGröße: Bestimmt durch Serialisieren des Wörterbuchs in ein MemoryStream und Ermitteln einer Byte-Länge (genau genug für unsere Zwecke).
Abgeschlossene Größenänderung: Die Zeit, die benötigt wird, um das interne Array von 150862 Elementen auf 312874 Elemente zu vergrößern. Wenn man bedenkt, dass jedes Element sequenziell über Array.CopyTo()
kopiert wird, ist das gar nicht schlecht.
Gesamtzeit zum Füllen: Zugegebenermaßen durch Protokollierung und ein OnResize
-Ereignis in der Quelle verzerrt; dennoch beeindruckend, 300.000 Ganzzahlen zu füllen, während 15-mal während des Vorgangs neu dimensioniert wird. Nur aus Neugier, wie lange würde die Gesamtzeit zum Füllen sein, wenn ich die Kapazität bereits kennen würde? 13ms
Also, was wäre, wenn Eintrag
eine Klasse wäre? Würden sich diese Zeiten oder Metriken wirklich so sehr unterscheiden?
Kapazität: 312874
MemGröße: 2660827 Bytes
Abgeschlossene Größenänderung: 26ms
Gesamtzeit zum Füllen: 964ms
Offensichtlich liegt der große Unterschied in der Größenänderung. Gibt es einen Unterschied, wenn das Dictionary mit der Kapazität initialisiert wird? Nicht genug, um besorgt zu sein ... 12ms.
Was passiert ist, dass Eintrag
eine Struktur ist und keine Initialisierung wie ein Referenztypus erfordert. Das ist sowohl die Schönheit als auch der Fluch des Werttypus. Um Eintrag
als Referenztypus zu verwenden, musste ich den folgenden Code einfügen:
/*
* Hinzugefügt, um die Initialisierung der Eintragselemente zu erfüllen -
* hier wird die zusätzliche Zeit beim Neudimensionieren des Eintrag-Arrays verbracht
* **/
for (int i = 0 ; i < prime ; i++)
{
destinationArray[i] = new Eintrag( );
}
/* *********************************************** */
Der Grund, warum ich jedes Arrayelement von Eintrag
als Referenztypus initialisieren musste, finden Sie unter MSDN: Strukturdesign. Kurz gesagt:
Stellen Sie keinen Standardkonstruktor für eine Struktur bereit.
Wenn eine Struktur einen Standardkonstruktor definiert, führt das Common Language Runtime automatisch den Standardkonstruktor für jedes Arrayelement aus, wenn Arrays der Struktur erstellt werden.
Einige Compiler, wie z.B. der C#-Compiler, erlauben es Strukturen nicht, Standardkonstruktoren zu haben.
Es ist eigentlich ziemlich einfach und wir werden uns von Asimovs Drei Gesetzen der Robotik inspirieren lassen:
- Die Struktur muss sicher zu verwenden sein
- Die Struktur muss ihre Funktion effizient erfüllen, es sei denn, dies würde gegen Regel #1 verstoßen
- Die Struktur muss während ihrer Verwendung intakt bleiben, es sei denn, ihre Zerstörung ist erforderlich, um Regel #1 zu erfüllen
...was nehmen wir daraus: Kurz gesagt, verantwortungsbewusst mit der Verwendung von Werttypen umgehen. Sie sind schnell und effizient, haben aber die Möglichkeit, viele unerwartete Verhaltensweisen zu verursachen, wenn sie nicht ordnungsgemäß gewartet werden (z.B. unbeabsichtigte Kopien).
0 Stimmen
Ich kann nur dem ersten Punkt zustimmen, Strukturen werden zum Beispiel sehr häufig in der Spieleprogrammierung verwendet.
305 Stimmen
System.Drawing.Rectangle
verletzt alle drei dieser Regeln.3 Stimmen
Ja, nun gut, zumindest Teile davon. Ich weiß, dass es für Teile von Spielen verwendet wird, wie NWN2 World Creator. C ist in der Regel immer noch das Kernelement (Engine). XNA Game Studio, Google es :)
7 Stimmen
Es gibt ziemlich viele kommerzielle Spiele, die in C# geschrieben sind, der Punkt ist, dass sie für optimierten Code verwendet werden.
2 Stimmen
Ich verwende sie manchmal für Zeichenfolgenwerte "enums".
4 Stimmen
Eine weitere Situation, in der Sie Strukturen verwenden sollten, ist, wenn Ihre Objekte keine Identität benötigen.
1 Stimmen
Ich bin immer nach dem Denken in der zweiten Bearbeitung der Frage gegangen: Wenn ich eine Handvoll verwandter Werttypen habe, erstelle ich eine Struktur. Ich könnte mehrere dieser kleinen Dateneinheiten haben und möchte wirklich nicht viele zusätzliche Klassendateien erstellen für das, was nur ein paar Dutzend Codezeilen wären.
1 Stimmen
Sie verwenden Strukturen, wenn Sie Ihre Klasse nicht vererben möchten. Das ist großartig, wenn Sie Objekte erstellen möchten, die nur Daten halten.
36 Stimmen
Strukturen bieten eine bessere Leistung, wenn Sie kleine Sammlungen von Werttypen haben, die Sie zusammen gruppieren möchten. Das passiert die ganze Zeit beim Spielprogrammieren, zum Beispiel wird ein Eckpunkt in einem 3D-Modell eine Position, eine Texturkoordinate und eine Normale haben, außerdem wird er in der Regel unveränderlich sein. Ein einzelnes Modell kann ein paar tausend Eckpunkte haben oder auch nur ein Dutzend, aber Strukturen bieten insgesamt weniger Overhead in diesem Nutzungsszenario. Das habe ich durch mein eigenes Engine-Design verifiziert.
0 Stimmen
Ich würde Strukturen für die Verbindung von nicht verwaltetem Code und leichtgewichtige statische Datensätze verwenden. Der Kompromiss zwischen Leistungssteigerung durch Speicherverwaltung und mangelnder Erweiterbarkeit im Design.
3 Stimmen
Das ist der völlig falsche Grund, um sich für Strukturen anstelle von Klassen zu entscheiden
6 Stimmen
@ErikForbes: Ich denke, dies wird allgemein als der größte BCL "oops" angesehen
0 Stimmen
@ChrisW: Der größte Defekt in
Rechteck
ist, dass seine Elemente Eigenschaften anstatt Felder sind. Die Verwendung von struct Eigenschaften, wenn Felder ausgereicht hätten, ist wahrscheinlich zu 95% für die Vorstellung verantwortlich, dass "veränderbare structs böse sind" (es gibt legitime Verwendungen für Eigenschaften-Setter in schreibgeschützten Strukturinstanzen; deshalb hat C# erst kürzlich ihre Verwendung verboten (legitime Anwendungsfälle ausschließend); Strukturen, die Elemente in Lese-Schreib-Eigenschaften einhüllten, verwandelten effektiv Code, der Compilerfehler hätte erzeugen sollen, in Code, der kompilierte, aber nicht funktionierte.0 Stimmen
Möglicher Duplikat von Wann sollte ich eine Struktur anstelle einer Klasse verwenden?
0 Stimmen
Ein interessanter Punkt könnte sein, wann sollte ich Strukturen mit Methoden verwenden, weil jeder Methode 1 Wort (16 Bytes) mehr in Bezug auf die Struktursumme .. also wird diese Struktur offensichtlich schwer genug sein, um Regel #2 zu brechen und die Leistungsfähigkeit zu beeinträchtigen
2 Stimmen
@ChrisW Stellt
System.Drawing.Rectangle
nicht einen einzigen Wert dar? Könnten Sie das bitte erklären?0 Stimmen
@MarsonMao Es stellt eine linke, eine rechte, eine obere, eine untere, eine Höhe und eine Breite dar (d. h. 6 Werte, zugegebenermaßen miteinander verwandte Werte).
8 Stimmen
@ChrisW Ich sehe, aber repräsentieren diese Werte nicht ein Rechteck, das heißt, einen "einzigen" Wert? Wie Vector3D oder Color, sie enthalten auch mehrere Werte, aber ich denke, sie repräsentieren einzelne Werte?
2 Stimmen
@MarsonMao Eines der bestimmenden Merkmale einer Struktur ist, dass es sich um eine Gruppe von verwandten Werten handelt, die als einzelner Wert behandelt werden sollen. Ein Rechteck erfüllt diese Eigenschaft sicherlich. Ich glaube, dass Chris versucht, Haare in der Wortwahl dieses Punktes zu spalten.
2 Stimmen
Beide Links sind defekt.
1 Stimmen
@weberc2 Chris sagt, diese Liste sollte nicht verwendet werden, um zu definieren, ob eine Struktur verwendet werden soll. Gegenbeispiel ist
Rechteck
: etwas, das auf jeden Fall eine Struktur sein sollte, aber trotzdem all dies verletzt. Daher sind sie keine guten Bedingungen.0 Stimmen
Das war eine gute Frage. Ich habe viel über die Unterschiede gelesen und hier habe ich schließlich die Antwort gefunden.