In .NET gibt es zwei Kategorien von Typen: Referenztypen und Werttypen .
Structs sind Werttypen und Klassen sind Referenztypen .
Der allgemeine Unterschied besteht darin, dass ein Referenztyp auf dem Heap und ein Werttyp inline lebt, dh überall dort, wo Ihre Variable oder Ihr Feld definiert ist.
Eine Variable, die einen Werttyp enthält, enthält den gesamten Wert des Werttyps. Für eine Struktur bedeutet dies, dass die Variable die gesamte Struktur mit all ihren Feldern enthält.
Eine Variable, die einen Referenztyp enthält, enthält einen Zeiger oder eine Referenz auf eine andere Stelle im Speicher, an der sich der tatsächliche Wert befindet.
Dies hat zunächst einen Vorteil:
- Werttypen enthalten immer einen Wert
- Referenztypen können enthalten null -Referenz, was bedeutet , dass sie sich beziehen , nicht im Moment überhaupt nichts
Intern werden Referenztypen als Zeiger implementiert. Wenn Sie wissen, dass und wie die Variablenzuweisung funktioniert, gibt es andere Verhaltensmuster:
- Durch Kopieren des Inhalts einer Variablen vom Typ Wert in eine andere Variable wird der gesamte Inhalt in die neue Variable kopiert, wodurch die beiden voneinander unterschieden werden. Mit anderen Worten, nach dem Kopieren wirken sich Änderungen an einem nicht auf das andere aus
- Kopieren des Inhalts eines Referenztyps Variable in eine andere Variable, kopiert die Referenz, die Mittel Sie jetzt zwei Verweise auf die gleiche haben woanders Speicherung der aktuellen Daten. Mit anderen Worten, nach dem Kopieren wirkt sich das Ändern der Daten in einer Referenz auch auf die andere aus, allerdings nur, weil Sie wirklich nur an beiden Stellen dieselben Daten betrachten
Wenn Sie Variablen oder Felder deklarieren, unterscheiden sich die beiden Typen folgendermaßen:
- Variable: Der Werttyp befindet sich auf dem Stapel, der Referenztyp befindet sich auf dem Stapel als Zeiger auf eine Stelle im Heapspeicher, an der sich der tatsächliche Speicher befindet (beachten Sie jedoch die Artikelserie von Eric Lipperts: Der Stapel ist ein Implementierungsdetail .)
- Klasse / Struktur-Feld: Werttyp lebt vollständig innerhalb der Art, Referenztyp lebt in der Art als Zeiger auf irgendwo im Heap - Speicher , wo die tatsächliche Speicherdauer.
Eine kurze Zusammenfassung von jedem:
Nur Klassen:
Nur Strukturen:
Sowohl Klassen als auch Strukturen:
quelle
c# struct memory overhead
und diese Antwort von Hans Passant gefunden, die besagt, dass nein, das ist auch nicht der Fall. Also, was meinst du?class
verwaltetem Speicher sind (vom Garbage Collector verarbeitet), Instanzen vonstruct
nicht .In .NET unterscheiden die Struktur- und Klassendeklarationen zwischen Referenztypen und Werttypen.
Wenn Sie einen Referenztyp übergeben, ist nur einer tatsächlich gespeichert. Der gesamte Code, der auf die Instanz zugreift, greift auf denselben zu.
Wenn Sie einen Werttyp übergeben, ist jeder eine Kopie. Der gesamte Code arbeitet an einer eigenen Kopie.
Dies kann anhand eines Beispiels gezeigt werden:
Für eine Klasse wäre das anders
Klassen können nichts sein - die Referenz kann auf eine Null zeigen.
Strukturen sind der tatsächliche Wert - sie können leer sein, aber niemals null. Aus diesem Grund haben Strukturen immer einen Standardkonstruktor ohne Parameter - sie benötigen einen 'Startwert'.
quelle
Unterschied zwischen Strukturen und Klassen:
quelle
Aus der Wahl von Microsoft zwischen Klasse und Struktur ...
quelle
Zusätzlich zu allen in den anderen Antworten beschriebenen Unterschieden:
Wenn Sie nach einem Video suchen, in dem alle Unterschiede erläutert werden, lesen Sie Teil 29 - C # -Tutorial - Unterschied zwischen Klassen und Strukturen in C # .
quelle
Instanzen von Klassen werden auf dem verwalteten Heap gespeichert. Alle Variablen, die eine Instanz enthalten, sind lediglich ein Verweis auf die Instanz auf dem Heap. Wenn Sie ein Objekt an eine Methode übergeben, wird eine Kopie der Referenz übergeben, nicht das Objekt selbst.
Strukturen (technisch gesehen Werttypen) werden überall dort gespeichert, wo sie verwendet werden, ähnlich wie bei einem primitiven Typ. Der Inhalt kann jederzeit und ohne Aufruf eines benutzerdefinierten Kopierkonstruktors zur Laufzeit kopiert werden. Um einen Werttyp an eine Methode zu übergeben, muss der gesamte Wert erneut kopiert werden, ohne dass anpassbarer Code aufgerufen werden muss.
Die Unterscheidung wird durch die C ++ / CLI-Namen verbessert: "ref class" ist eine Klasse wie zuerst beschrieben, "value class" ist eine Klasse wie zuerst beschrieben. Die von C # verwendeten Schlüsselwörter "class" und "struct" müssen einfach gelernt werden.
quelle
quelle
Struktur gegen Klasse
Eine Struktur ist ein Werttyp, daher wird sie auf dem Stapel gespeichert, eine Klasse ist jedoch ein Referenztyp und wird auf dem Heap gespeichert.
Eine Struktur unterstützt keine Vererbung und keinen Polymorphismus, aber eine Klasse unterstützt beide.
Standardmäßig sind alle Strukturelemente öffentlich, aber Klassenmitglieder sind standardmäßig privater Natur.
Da eine Struktur ein Werttyp ist, können wir einem Strukturobjekt keine Null zuweisen, dies ist jedoch für eine Klasse nicht der Fall.
quelle
Zu den anderen Antworten kommt noch ein grundlegender Unterschied hinzu, nämlich die Art und Weise, wie die Daten in Arrays gespeichert werden, da dies einen erheblichen Einfluss auf die Leistung haben kann.
Ein Array von Strukturen sieht also im Speicher so aus
[struct][struct][struct][struct][struct][struct][struct][struct]
Während eine Reihe von Klassen so aussieht
[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]
Bei einem Array von Klassen werden die Werte, an denen Sie interessiert sind, nicht im Array, sondern an anderer Stelle im Speicher gespeichert.
Für die überwiegende Mehrheit der Anwendungen spielt dieser Unterschied keine Rolle. Bei Hochleistungscode wirkt sich dies jedoch auf die Lokalität der Daten im Speicher aus und hat einen großen Einfluss auf die Leistung des CPU-Cache. Die Verwendung von Klassen, wenn Sie Strukturen hätten verwenden können / sollen, erhöht die Anzahl der Cache-Fehler auf der CPU massiv.
Das Langsamste, was eine moderne CPU tut, ist, keine Zahlen zu knacken, sondern Daten aus dem Speicher abzurufen, und ein L1-Cache-Treffer ist um ein Vielfaches schneller als das Lesen von Daten aus dem RAM.
Hier ist ein Code, den Sie testen können. Auf meinem Computer dauert das Durchlaufen des Klassenarrays ~ 3x länger als das Strukturarray.
quelle
Um es zu vervollständigen, gibt es einen weiteren Unterschied bei der Verwendung der
Equals
Methode, die von allen Klassen und Strukturen geerbt wird.Nehmen wir an, wir haben eine Klasse und eine Struktur:
und in der Hauptmethode haben wir 4 Objekte.
Dann:
So werden Strukturen für numerische ähnliche Objekte geeignet, wie Punkte (save x und y - Koordinaten). Und Klassen sind für andere geeignet. Selbst wenn 2 Personen den gleichen Namen, die gleiche Größe, das gleiche Gewicht haben ... sind es immer noch 2 Personen.
quelle
Nun, für den Anfang wird eine Struktur eher als Wert als als Referenz übergeben. Strukturen eignen sich für relativ einfache Datenstrukturen, während Klassen aus architektonischer Sicht über Polymorphismus und Vererbung viel flexibler sind.
Andere können Ihnen wahrscheinlich mehr Details geben als ich, aber ich verwende Strukturen, wenn die Struktur, die ich anstrebe, einfach ist.
quelle
Neben dem grundlegenden Unterschied des Zugriffsspezifizierers und den wenigen oben genannten möchte ich einige der Hauptunterschiede, einschließlich einiger der oben genannten, mit einem Codebeispiel mit Ausgabe hinzufügen, das eine klarere Vorstellung von der Referenz und dem Wert gibt
Strukturen:
Klasse:
Codebeispiel
Ausgabe
Der Anfangswert von Struct Object ist: 10
Inside Struct-Methode Der Inside Method-Wert des Struct-Objekts beträgt: 20
Nach dem Methodenaufruf beträgt der Wert von Struct Object: 10
Der Anfangswert des Klassenobjekts ist: 10
Inside Class-Methode Der Inside Method-Wert des Klassenobjekts beträgt: 20
Nach Methode Aufrufwert von Class Object ist: 20
Hier sehen Sie deutlich den Unterschied zwischen Call by Value und Call by Reference.
quelle
In einer Klasse deklarierte Ereignisse haben ihren + = und - = Zugriff automatisch über eine Sperre (dies) gesperrt, um sie threadsicher zu machen (statische Ereignisse werden für den Typ der Klasse gesperrt). Bei Ereignissen, die in einer Struktur deklariert sind, wird der Zugriff von + = und - = nicht automatisch gesperrt. Eine Sperre (dies) für eine Struktur würde nicht funktionieren, da Sie nur einen Referenztypausdruck sperren können.
Das Erstellen einer Strukturinstanz kann keine Garbage Collection verursachen (es sei denn, der Konstruktor erstellt direkt oder indirekt eine Referenztypinstanz), während das Erstellen einer Referenztypinstanz eine Garbage Collection verursachen kann.
Eine Struktur verfügt immer über einen integrierten öffentlichen Standardkonstruktor.
Dies bedeutet, dass eine Struktur immer instanziierbar ist, während eine Klasse dies möglicherweise nicht ist, da alle ihre Konstruktoren privat sein können.
Eine Struktur kann keinen Destruktor haben. Ein Destruktor ist nur eine Überschreibung des Objekts. In Verkleidung abschließen, und Strukturen als Werttypen unterliegen keiner Speicherbereinigung.
Eine Struktur ist implizit versiegelt, eine Klasse nicht.
Eine Struktur kann nicht abstrakt sein, eine Klasse kann.
Eine Struktur kann: base () in ihrem Konstruktor nicht aufrufen, während eine Klasse ohne explizite Basisklasse dies kann.
Eine Struktur kann keine andere Klasse erweitern, eine Klasse kann.
Eine Struktur kann keine geschützten Mitglieder (z. B. Felder, verschachtelte Typen) deklarieren, die eine Klasse kann.
Eine Struktur kann keine abstrakten Funktionsmitglieder deklarieren, eine abstrakte Klasse kann dies.
Eine Struktur kann keine virtuellen Funktionsmitglieder deklarieren, eine Klasse kann dies.
Eine Struktur kann keine versiegelten Funktionsmitglieder deklarieren, eine Klasse kann dies.
Eine Struktur kann keine Überschreibungsfunktionsmitglieder deklarieren, eine Klasse kann dies.
Die einzige Ausnahme von dieser Regel besteht darin, dass eine Struktur die virtuellen Methoden von System.Object, nämlich Equals (), GetHashCode () und ToString (), überschreiben kann.
quelle
Object
, der einen Verweis auf eine Box-Kopie der Struktur enthalten würde.Wie bereits erwähnt: Klassen sind Referenztypen, während Strukturen Werttypen mit allen Konsequenzen sind.
Als Faustregel empfiehlt Framework Design Guidelines die Verwendung von Strukturen anstelle von Klassen, wenn:
quelle
Es gibt einen interessanten Fall eines "Klasse gegen Struktur" -Puzzles - eine Situation, in der Sie mehrere Ergebnisse der Methode zurückgeben müssen: Wählen Sie die zu verwendende aus. Wenn Sie die ValueTuple-Story kennen, wissen Sie, dass ValueTuple (struct) hinzugefügt wurde, da es effektiver sein sollte als Tuple (class). Aber was bedeutet das in Zahlen? Zwei Tests: Einer ist Struktur / Klasse mit 2 Feldern, der andere mit Struktur / Klasse mit 8 Feldern (mit einer Dimension von mehr als 4 - Klasse sollte effektiver werden als Struktur in Bezug auf Prozessorticks, aber natürlich sollte auch die GC-Last berücksichtigt werden ).
PS Ein weiterer Benchmark für den speziellen Fall "Struktur oder Klasse mit Sammlungen" ist dort: https://stackoverflow.com/a/45276657/506147
Codetest:
quelle
Dies ist wahr, beachten Sie jedoch auch, dass ab .NET 2-Strukturen eine Nullable-Version unterstützen und C # syntaktischen Zucker liefert, um die Verwendung zu vereinfachen.
quelle
(object)(default(int?)) == null
dass Sie mit keinem anderen Werttyp etwas anfangen können, da hier mehr als nur Zucker vor sich geht. Der einzige Zucker istint?
fürNullable<int>
.Jede Variable oder jedes Feld eines primitiven Werttyps oder Strukturtyps enthält eine eindeutige Instanz dieses Typs, einschließlich aller seiner Felder (öffentlich und privat). Im Gegensatz dazu können Variablen oder Felder von Referenztypen null enthalten oder sich auf ein an anderer Stelle gespeichertes Objekt beziehen, auf das auch eine beliebige Anzahl anderer Referenzen existieren kann. Die Felder einer Struktur werden an derselben Stelle gespeichert wie die Variable oder das Feld dieses Strukturtyps, die sich entweder auf dem Stapel befinden oder Teil eines anderen Heap-Objekts sein können.
Wenn Sie eine Variable oder ein Feld eines primitiven Werttyps erstellen, wird es mit einem Standardwert erstellt. Durch das Erstellen einer Variablen oder eines Felds eines Strukturtyps wird eine neue Instanz erstellt, wobei alle darin enthaltenen Felder standardmäßig erstellt werden. Das Erstellen einer neuen Instanz eines Referenztyps beginnt damit, dass alle darin enthaltenen Felder standardmäßig erstellt werden und anschließend je nach Typ optional zusätzlicher Code ausgeführt wird.
Durch Kopieren einer Variablen oder eines Felds eines primitiven Typs in ein anderes wird der Wert kopiert. Durch Kopieren einer Variablen oder eines Felds vom Strukturtyp in ein anderes werden alle Felder (öffentlich und privat) der ersteren Instanz in die letztere Instanz kopiert. Wenn Sie eine Variable oder ein Feld des Referenztyps in ein anderes kopieren, verweist die letztere auf dieselbe Instanz wie die erstere (falls vorhanden).
Es ist wichtig zu beachten, dass in einigen Sprachen wie C ++ das semantische Verhalten eines Typs unabhängig davon ist, wie es gespeichert wird, dies gilt jedoch nicht für .NET. Wenn ein Typ eine Semantik für veränderbare Werte implementiert, werden beim Kopieren einer Variablen dieses Typs in eine andere die Eigenschaften der ersten in eine andere Instanz kopiert, auf die von der zweiten verwiesen wird, und wenn ein Mitglied der zweiten zum Mutieren verwendet wird, wird diese zweite Instanz geändert , aber nicht der erste. Wenn ein Typ eine veränderbare Referenzsemantik implementiert, wirkt sich das Kopieren einer Variablen in eine andere und die Verwendung eines Mitglieds der zweiten zum Mutieren des Objekts auf das Objekt aus, auf das sich die erste Variable bezieht. Typen mit unveränderlicher Semantik erlauben keine Mutation, daher spielt es semantisch keine Rolle, ob beim Kopieren eine neue Instanz oder ein anderer Verweis auf die erste erstellt wird.
In .NET können Werttypen eine der oben genannten Semantiken implementieren, vorausgesetzt, alle ihre Felder können dies ebenfalls. Ein Referenztyp kann jedoch nur veränderbare Referenzsemantik oder unveränderliche Semantik implementieren. Werttypen mit Feldern veränderlicher Referenztypen beschränken sich entweder auf die Implementierung veränderlicher Referenzsemantik oder auf seltsame Hybridsemantik.
quelle