Was sind Ihre Faustregeln für die Verwendung von Strukturen im Vergleich zu Klassen? Ich denke an die C # -Definition dieser Begriffe, aber wenn Ihre Sprache ähnliche Konzepte hat, würde ich gerne auch Ihre Meinung hören.
Ich neige dazu, Klassen für fast alles zu verwenden, und benutze Strukturen nur, wenn etwas sehr simpel ist und ein Werttyp sein sollte, wie z. B. eine PhoneNumber oder ähnliches. Dies scheint jedoch eine relativ geringe Verwendung zu sein, und ich hoffe, dass es interessantere Anwendungsfälle gibt.
Antworten:
Als allgemeine Regel gilt, dass Strukturen kleine, einfache (einstufige) Sammlungen verwandter Eigenschaften sein sollten, die nach ihrer Erstellung unveränderlich sind. Verwenden Sie für alles andere eine Klasse.
C # hat den Vorteil, dass Strukturen und Klassen außer dem Schlüsselwort defining keine expliziten Unterschiede in der Deklaration aufweisen. Wenn Sie also das Gefühl haben, eine Struktur zu einer Klasse "upgraden" oder umgekehrt eine Klasse zu einer Struktur "downgraden" zu müssen, müssen Sie meistens das Schlüsselwort ändern (es gibt ein paar andere Fallstricke; Strukturen können nicht abgeleitet werden) aus einer anderen Klasse oder einem anderen Strukturtyp und sie können keinen parameterlosen Standardkonstruktor explizit definieren).
Ich sage "meistens", weil das Wichtigste, was man über Strukturen wissen muss, ist, dass es anderthalb Mal schmerzen kann, wenn man sie wie Klassen (Referenztypen) behandelt, weil sie Werttypen sind. Insbesondere das Ändern der Eigenschaften einer Struktur kann zu unerwartetem Verhalten führen.
Angenommen, Sie haben eine Klasse SimpleClass mit zwei Eigenschaften, A und B. Sie instanziieren eine Kopie dieser Klasse, initialisieren A und B und übergeben die Instanz dann an eine andere Methode. Diese Methode ändert A und B weiter. Zurück in der aufrufenden Funktion (derjenigen, die die Instanz erstellt hat) haben die A und B Ihrer Instanz die Werte, die ihnen von der aufgerufenen Methode zugewiesen wurden.
Jetzt machen Sie es eine Struktur. Die Eigenschaften sind noch veränderbar. Sie führen dieselben Operationen mit derselben Syntax wie zuvor aus, aber jetzt befinden sich die neuen Werte von A und B nach dem Aufruf der Methode nicht in der Instanz. Was ist passiert? Nun, Ihre Klasse ist jetzt eine Struktur, was bedeutet, dass es sich um einen Wertetyp handelt. Wenn Sie einen Werttyp an eine Methode übergeben, wird standardmäßig (ohne out- oder ref-Schlüsselwort) "nach Wert" übergeben. Eine flache Kopie der Instanz wird für die Verwendung durch die Methode erstellt und dann zerstört, wenn die Methode abgeschlossen ist und die ursprüngliche Instanz intakt bleibt.
Dies wird noch verwirrender, wenn Sie einen Referenztyp als Mitglied Ihrer Struktur haben (nicht unzulässig, aber in praktisch allen Fällen äußerst schlechte Praxis); Die Klasse wird nicht geklont (nur der Verweis der Struktur darauf). Änderungen an der Struktur wirken sich nicht auf das ursprüngliche Objekt aus. Änderungen an der Unterklasse der Struktur wirken sich jedoch auf die Instanz aus, die vom aufrufenden Code stammt. Dies kann sehr leicht veränderbare Strukturen in sehr inkonsistente Zustände versetzen, die weit entfernt vom eigentlichen Problem Fehler verursachen können.
Aus diesem Grund fordert praktisch jede Autorität in C #, Ihre Strukturen immer unveränderlich zu machen. Erlauben Sie dem Konsumenten, die Werte der Eigenschaften nur beim Erstellen eines Objekts anzugeben, und bieten Sie niemals die Möglichkeit, die Werte dieser Instanz zu ändern. Readonly-Felder oder Get-Only-Eigenschaften sind die Regel. Wenn der Konsument den Wert ändern möchte, kann er ein neues Objekt basierend auf den Werten des alten Objekts mit den gewünschten Änderungen erstellen oder eine Methode aufrufen, die dasselbe tut. Dies zwingt sie, eine einzelne Instanz Ihrer Struktur als einen begrifflichen "Wert" zu behandeln, der unteilbar und von allen anderen verschieden (aber möglicherweise allen anderen gleichwertig) ist. Wenn sie eine Operation für einen von Ihrem Typ gespeicherten "Wert" ausführen, erhalten sie einen neuen "Wert", der sich von ihrem Anfangswert unterscheidet.
Ein gutes Beispiel finden Sie unter DateTime. Sie können keines der Felder einer DateTime-Instanz direkt zuweisen. Sie müssen entweder eine neue erstellen oder eine Methode für die vorhandene aufrufen, die eine neue Instanz erzeugt. Dies liegt daran, dass Datum und Uhrzeit ein "Wert" sind, wie die Zahl 5, und eine Änderung der Zahl 5 einen neuen Wert ergibt, der nicht 5 ist. Nur weil 5 + 1 = 6 bedeutet, dass 5 jetzt 6 ist weil du 1 hinzugefügt hast. DateTimes funktionieren genauso. 12:00 wird nicht zu 12:01, wenn Sie eine Minute hinzufügen. Stattdessen erhalten Sie einen neuen Wert 12:01, der sich von 12:00 unterscheidet. Wenn dies ein logischer Sachverhalt für Ihren Typ ist (gute konzeptionelle Beispiele, die in .NET nicht integriert sind, sind Money, Distance, Weight und andere Mengen einer Maßeinheit, bei denen Operationen alle Teile des Werts berücksichtigen müssen), dann benutze eine Struktur und gestalte sie entsprechend. Verwenden Sie in den meisten anderen Fällen, in denen die Unterelemente eines Objekts unabhängig voneinander veränderbar sein sollen, eine Klasse.
quelle
Die Antwort: "Verwenden Sie eine Struktur für reine Datenkonstrukte und eine Klasse für Objekte mit Operationen" ist definitiv falsch. Wenn eine Struktur eine große Anzahl von Eigenschaften enthält, ist eine Klasse fast immer geeigneter. Aus Sicht der Effizienz sagt Microsoft oft, wenn Ihr Typ größer als 16 Byte ist, sollte er eine Klasse sein.
https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes
quelle
Der wichtigste Unterschied zwischen a
class
und astruct
ist, was in der folgenden Situation passiert:Auf was soll sich die letzte Aussage auswirken
thing1.somePropertyOrField
? Wenn es sich beiThing
einer Struktur undsomePropertyOrField
um ein verfügbares öffentliches Feld handelt, werden die Objektething1
undthing2
voneinander "getrennt", sodass sich die letztere Anweisung nicht auswirktthing1
. WennThing
es sich um eine Klasse handelt, werdenthing1
undthing2
aneinander angehängt, und so wird die letztere Anweisung geschriebenthing1.somePropertyOrField
. Eine Struktur sollte in Fällen verwendet werden, in denen die erstere Semantik sinnvoller wäre, und eine Klasse sollte in Fällen verwendet werden, in denen die letztere Semantik sinnvoller wäre.Beachten Sie, dass einige Leute raten, dass der Wunsch, etwas veränderbar zu machen, ein Argument dafür ist, dass es die Klasse ist. Ich würde jedoch das Gegenteil vermuten: Wenn etwas existiert, um einige Daten zu speichern, wird es veränderbar sein, und Wenn nicht klar ist, ob Instanzen an irgendetwas gebunden sind, sollte das Ding eine Struktur sein (wahrscheinlich mit exponierten Feldern), um zu verdeutlichen, dass Instanzen an nichts anderes gebunden sind.
Betrachten Sie zum Beispiel die Anweisungen:
Ändert die zweite Anweisung die in gespeicherten Informationen
myPeople
? WennPerson
es sich um eine Struktur mit exponierten Feldern handelt, wird dies nicht der Fall sein , und die Tatsache, dass dies nicht der Fall ist, ist eine offensichtliche Folge davon, dass es sich um eine Struktur mit exponierten Feldern handelt . WennPerson
es eine Struktur ist und man sie aktualisierenmyPeople
möchte, müsste man eindeutig so etwas tunmyPeople.UpdatePerson("123-45-6789", somePerson)
. WennPerson
es sich jedoch um eine Klasse handelt, ist es möglicherweise viel schwieriger zu bestimmen, ob der obige Code den Inhalt von niemals aktualisierenMyPeople
, immer aktualisieren oder manchmal aktualisieren würde.In Bezug auf die Vorstellung, dass Strukturen "unveränderlich" sein sollten, bin ich im Allgemeinen anderer Meinung. Es gibt gültige Anwendungsfälle für "unveränderliche" Strukturen (bei denen Invarianten in einem Konstruktor erzwungen werden), aber es ist umständlich, verschwenderisch und eher dazu geneigt, Fehler zu verursachen, als sie nur anzuzeigen Felder direkt.
PhoneNumber
Stellen Sie sich zum Beispiel eine Struktur vor, deren Felder unter anderemAreaCode
und umfassenExchange
, und nehmen Sie an, eine hat einList<PhoneNumber>
. Der Effekt des Folgenden sollte ziemlich klar sein:Beachten Sie, dass der obige Code keine Felder
PhoneNumber
außerAreaCode
und kennt oder kümmertExchange
. WennPhoneNumber
es sich um eine sogenannte "unveränderliche" Struktur handeln würde, müsste entwederwithXX
für jedes Feld eine Methode angegeben werden, die eine neue Strukturinstanz zurückgibt, die den übergebenen Wert im angegebenen Feld enthält, oder es wäre erforderlich für Code wie den obigen, um über jedes Feld in der Struktur Bescheid zu wissen. Nicht gerade ansprechend.Übrigens gibt es mindestens zwei Fälle, in denen Strukturen möglicherweise Verweise auf veränderbare Typen enthalten.
Im ersten Szenario ist die IDENTITÄT des Objekts unveränderlich. Im zweiten Fall erzwingt die Klasse des verschachtelten Objekts möglicherweise keine Unveränderlichkeit, während die Struktur, die die Referenz enthält, dies tut.
quelle
Ich neige dazu, Strukturen nur dann zu verwenden, wenn eine Methode benötigt wird, wodurch eine Klasse zu "schwer" erscheint. Daher behandle ich Strukturen manchmal als leichte Objekte. ACHTUNG: Dies funktioniert nicht immer und ist auch nicht immer das Beste. Es kommt also wirklich auf die Situation an!
ZUSÄTZLICHE RESSOURCEN
Für eine formellere Erklärung sagt MSDN: http://msdn.microsoft.com/en-us/library/ms229017.aspx Dieser Link überprüft, was ich oben erwähnt habe. Strukturen sollten nur für die Unterbringung einer kleinen Datenmenge verwendet werden.
quelle
Verwenden Sie eine Struktur für reine Datenkonstrukte und eine Klasse für Objekte mit Operationen.
Wenn Ihre Datenstruktur keine Zugriffskontrolle benötigt und keine anderen speziellen Vorgänge als get / set hat, verwenden Sie eine Struktur. Dies macht deutlich, dass diese Struktur nur ein Container für Daten ist.
Wenn Ihre Struktur Vorgänge enthält, die die Struktur auf komplexere Weise ändern, verwenden Sie eine Klasse. Eine Klasse kann auch mit anderen Klassen über Argumente zu Methoden interagieren.
Betrachten Sie es als den Unterschied zwischen einem Ziegelstein und einem Flugzeug. Ein Ziegelstein ist eine Struktur; Es hat Länge, Breite und Höhe. Es kann nicht viel bewirken. Ein Flugzeug könnte mehrere Operationen haben, die es irgendwie mutieren, wie das Bewegen von Steuerflächen und das Ändern des Triebwerksschubs. Es wäre besser als eine Klasse.
quelle