1628 Stimmen

Wann sollte ich in C# eine Struktur anstelle einer Klasse verwenden?

Wann sollten Sie in C# struct und nicht class verwenden? Mein konzeptionelles Modell besagt, dass structs verwendet werden, wenn das Element nur eine Sammlung von Wertetypen ist. Eine Möglichkeit, sie alle logisch zu einem zusammenhängenden Ganzen zusammenzufassen.

Ich bin auf diese Regeln hier gestoßen:

  • Ein struct sollte einen einzigen Wert repräsentieren.
  • Ein struct sollte einen Speicherbedarf von weniger als 16 Bytes haben.
  • Ein struct sollte nach der Erstellung nicht verändert werden.

Funktionieren diese Regeln? Was bedeutet ein struct semantisch?

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 :)

14voto

Jason Williams Punkte 2568

Ein Struktur ist ein Werttyp. Wenn Sie einer Struktur eine neue Variable zuweisen, enthält die neue Variable eine Kopie des Originals.

public struct IntStruct {
    public int Value {get; set;}
}

Die Ausführung des folgenden führt zu 5 Instanzen der Struktur im Speicher:

var struct1 = new IntStruct() { Value = 0 }; // original
var struct2 = struct1;  // Eine Kopie wird erstellt
var struct3 = struct2;  // Eine Kopie wird erstellt
var struct4 = struct3;  // Eine Kopie wird erstellt
var struct5 = struct4;  // Eine Kopie wird erstellt

// HINWEIS: Eine "Kopie" wird erstellt, wenn Sie eine Struktur als Methodenparameter übergeben.
// Verwenden Sie das ref-Schlüsselwort, um die "Kopie" zu vermeiden.

// Obwohl Strukturen darauf ausgelegt sind, weniger Systemressourcen zu verbrauchen als Klassen. Wenn sie falsch verwendet werden, können sie erheblich mehr verbrauchen.

Eine Klasse ist ein Verweistyp. Wenn Sie einer Klasse eine neue Variable zuweisen, enthält die Variable einen Verweis auf das Originalobjekt der Klasse.

public class IntClass {
    public int Value {get; set;}
}

Die Ausführung des folgenden führt zu nur einer Instanz des Klassenobjekts im Speicher.

var class1 = new IntClass() { Value = 0 };
var class2 = class1;  // Ein Verweis auf class1 wird erstellt
var class3 = class2;  // Ein Verweis auf class1 wird erstellt
var class4 = class3;  // Ein Verweis auf class1 wird erstellt
var class5 = class4;  // Ein Verweis auf class1 wird erstellt

Strukturen können das Risiko eines Programmfehlers erhöhen. Wenn ein Wertobjekt wie ein veränderliches Referenzobjekt behandelt wird, kann ein Entwickler überrascht sein, wenn unerwartete Änderungen verloren gehen.

var struct1 = new IntStruct() { Value = 0 };
var struct2 = struct1;
struct2.Value = 1;
// An diesem Punkt könnte ein Entwickler überrascht sein, wenn
// struct1.Value 0 und nicht 1 ist

11voto

supercat Punkte 72939

Strukturtypen in C# oder anderen .NET-Sprachen werden in der Regel verwendet, um Dinge zu halten, die sich wie Gruppen fester Größen von Werten verhalten sollen. Ein nützlicher Aspekt von Strukturtypen ist, dass die Felder einer Strukturtyp-Instanz geändert werden können, indem der Speicherort, in dem sie gehalten wird, geändert wird, und auf keine andere Weise. Es ist möglich, eine Struktur so zu codieren, dass der einzige Weg, um ein Feld zu verändern, darin besteht, eine vollständig neue Instanz zu konstruieren und dann eine Strukturzuweisung zu verwenden, um alle Felder des Ziels zu verändern, indem sie mit Werten aus der neuen Instanz überschrieben werden, aber es sei denn, eine Struktur bietet keine Möglichkeit der Erstellung einer Instanz, bei der ihre Felder nicht standardmäßige Werte haben, werden alle ihre Felder veränderbar sein, wenn die Struktur selbst an einem veränderbaren Ort gespeichert ist.

Beachten Sie, dass es möglich ist, einen Strukturtyp so zu entwerfen, dass er sich im Wesentlichen wie ein Klassen-Typ verhält, wenn die Struktur ein privates Feld vom Typ Klasse enthält und ihre eigenen Mitglieder auf die des umschlossenen Klassenobjekts umleitet. Zum Beispiel könnte eine PersonCollection Eigenschaften wie SortedByName und SortedById anbieten, die beide auf eine "unveränderliche" Referenz zu einer PersonCollection (in ihrem Konstruktor festgelegt) verweisen und GetEnumerator implementieren, indem sie entweder creator.GetNameSortedEnumerator oder creator.GetIdSortedEnumerator aufrufen. Solche Strukturen würden sich weitgehend wie eine Referenz auf eine PersonCollection verhalten, mit der Ausnahme, dass ihre GetEnumerator Methoden an verschiedene Methoden in der PersonCollection gebunden wären. Man könnte auch eine Struktur haben, die einen Teil eines Arrays umschließt (zum Beispiel könnte man eine ArrayRange Struktur definieren, die ein T[] namens Arr, ein int Offset und eine int Length hält, mit einer indexierten Eigenschaft, die für einen Index idx im Bereich von 0 bis Length-1 auf Arr[idx+Offset] zugreifen würde). Leider erlauben aktuelle Compiler-Versionen keine Operationen wie foo[3] += 4;, wenn foo eine schreibgeschützte Instanz einer solchen Struktur ist, weil sie keinen Weg haben zu bestimmen, ob solche Operationen versuchen würden, auf Felder von foo zu schreiben.

Es ist auch möglich, eine Struktur so zu entwerfen, dass sie sich wie ein Werttyp verhält, der eine variablengroße Sammlung hält (die immer dann erscheinen wird, wenn die Struktur kopiert wird), aber der einzige Weg, dies zu erreichen, besteht darin, sicherzustellen, dass kein Objekt, auf das sich die Struktur bezieht, jemals etwas ausgesetzt wird, das es mutieren könnte. Zum Beispiel könnte man eine arrayähnliche Struktur haben, die ein privates Array hält, und deren indexierte "put"-Methode ein neues Array erstellt, dessen Inhalt dem des Originals ähnelt, außer einem geänderten Element. Leider kann es oft schwierig sein, solche Strukturen effizient auszuführen. Während es Zeiten gibt, in denen Struktursemantik praktisch sein kann (z.B. kann es besser sein, eine arrayähnliche Sammlung an eine Routine zu übergeben, wobei sowohl der Aufrufer als auch der Aufgerufene wissen, dass der externe Code die Sammlung nicht verändern wird, als zu verlangen, dass sowohl der Aufrufer als auch der Aufgerufene defensiv alle Daten kopieren, die sie erhalten), ist die Anforderung, dass Klassenverweise auf Objekte verweisen, die niemals mutiert werden, oft eine recht strenge Einschränkung.

10voto

SnapJag Punkte 797

Nah - Ich stimme nicht vollständig mit den Regeln überein. Sie sind gute Richtlinien, um Leistung und Standardisierung zu berücksichtigen, aber nicht im Hinblick auf die Möglichkeiten.

Wie Sie an den Antworten sehen können, gibt es viele kreative Möglichkeiten, sie zu verwenden. Diese Richtlinien müssen also nur das sein, immer im Sinne von Leistung und Effizienz.

In diesem Fall verwende ich Klassen, um reale Objekte in ihrer größeren Form darzustellen, ich verwende Strukturen, um kleinere Objekte zu repräsentieren, die genauer definierte Verwendungszwecke haben. Wie Sie es gesagt haben, "ein zusammenhängenderes Ganzes". Das Schlüsselwort ist zusammenhängend. Die Klassen werden mehr objektorientierte Elemente sein, während Strukturen einige dieser Eigenschaften haben können, jedoch in kleinerem Maßstab. Meiner Meinung nach.

Ist benutze sie viel in den Treeview- und Listview-Tags, wo häufig verwendete statische Attribute sehr schnell abgerufen werden können. Mir ist es immer schwer gefallen, diese Informationen auf andere Weise zu erhalten. Beispielsweise verwende ich in meinen Datenbankanwendungen ein Treeview, wo ich Tabellen, SPs, Funktionen oder andere Objekte habe. Ich erstelle und fülle meine Struktur, platziere sie im Tag, ziehe sie heraus, erhalte die Daten der Auswahl und so weiter. Das würde ich nicht mit einer Klasse machen!

Ich versuche, sie klein zu halten, in Einzelinstanzsituationen zu verwenden und sie nicht zu ändern. Es ist wichtig, sich über Speicherplatz, Zuweisung und Leistung im Klaren zu sein. Und Tests sind so notwendig.

0 Stimmen

Strukturen können sinnvoll genutzt werden, um leichtgewichtige unveränderliche Objekte darzustellen, oder sie können sinnvoll genutzt werden, um feste Gruppen von verwandten, aber unabhängigen Variablen darzustellen (z. B. die Koordinaten eines Punktes). Der Rat auf dieser Seite ist gut für Strukturen, die dazu dienen sollen, ersteren Zweck zu erfüllen, jedoch falsch für Strukturen, die dazu dienen sollen, letzteren Zweck zu erfüllen. Meine derzeitige Überlegung ist, dass Strukturen, die private Felder haben, im Allgemeinen der angegebenen Beschreibung entsprechen sollten, aber viele Strukturen ihren gesamten Zustand über öffentliche Felder offenlegen sollten.

0 Stimmen

Wenn die Spezifikation für einen "3D-Punkt"-Typ angibt, dass sein gesamter Zustand über lesbare Elemente x, y und z freigelegt wird und es möglich ist, eine Instanz mit beliebiger Kombination von double Werten für diese Koordinaten zu erstellen, würde eine solche Spezifikation dazu führen, dass sie sich semantisch identisch wie ein struktur mit freigelegten Feldern verhält, abgesehen von einigen Details des Mehrthread-Verhaltens (die unveränderliche Klasse wäre in einigen Fällen besser, während die struktur mit freigelegten Feldern in anderen besser wäre; eine sogenannte "unveränderliche" struktur wäre in jedem Fall schlechter).

9voto

rockXrock Punkte 3363

Meine Regel lautet:

1. Immer Klassen verwenden;

2. Wenn es Leistungsprobleme gibt, versuche ich, einige Klassen je nach den Regeln, die @IAbstract erwähnt hat, in Strukturen zu ändern und dann einen Test durchzuführen, um zu sehen, ob diese Änderungen die Leistung verbessern können.

0 Stimmen

Ein bedeutender Anwendungsfall, den Microsoft ignoriert, ist, wenn man eine Variable vom Typ Foo nutzen möchte, um eine feste Sammlung unabhängiger Werte zu umschließen (z.B. Koordinaten eines Punktes), die manchmal als Gruppe übergeben und manchmal unabhängig voneinander geändert werden sollen. Ich habe kein Muster für die Verwendung von Klassen gefunden, das beide Zwecke annähernd so gut kombiniert wie eine einfache strukturierte Variable mit freiliegenden Feldern (die, als feste Sammlung unabhängiger Variablen, perfekt geeignet ist).

1 Stimmen

@supercat: Ich denke, es ist nicht ganz fair, Microsoft dafür verantwortlich zu machen. Das eigentliche Problem hier ist, dass C# als objektorientierte Sprache sich einfach nicht auf einfache Datentypen konzentriert, die nur Daten ohne viel Verhalten offenlegen. C# ist keine Mehrparadigmen-Sprache in dem Maße wie z.B. C++. Dennoch glaube ich auch, dass sehr wenige Menschen rein objektorientiert programmieren, daher ist C# vielleicht eine zu idealistische Sprache. (Ich selbst habe kürzlich auch angefangen, public readonly Felder in meinen Typen freizulegen, weil es einfach zu viel Arbeit ist, schreibgeschützte Eigenschaften für praktisch keinen Nutzen zu erstellen.)

1 Stimmen

@stakx: Es besteht kein Bedarf dafür, sich auf solche Typen "zu konzentrieren"; es würde ausreichen, sie als das anzuerkennen, was sie sind. Die größte Schwäche von C# im Hinblick auf Strukturen ist auch sein größtes Problem in vielen anderen Bereichen: Die Sprache bietet unzureichende Möglichkeiten, um anzuzeigen, wann bestimmte Transformationen angemessen sind oder nicht, und das Fehlen solcher Einrichtungen führt zu unglücklichen Designentscheidungen. Zum Beispiel stammt 99% der „veränderbaren Strukturen sind böse“ davon, dass der Compiler MyListOfPoint[3].Offset(2,3); in var temp=MyListOfPoint[3]; temp.Offset(2,3); umwandelt, eine Transformation, die falsch ist, wenn sie angewendet wird...

9voto

J_hajian_nzd Punkte 177

Eine Klasse ist ein Verweistyp. Wenn ein Objekt der Klasse erstellt wird, hält die Variable, der das Objekt zugewiesen ist, nur einen Verweis auf diesen Speicher. Wenn der Objektverweis einer neuen Variable zugewiesen wird, bezieht sich die neue Variable auf das originale Objekt. Änderungen, die über eine Variable vorgenommen werden, spiegeln sich in der anderen Variable wider, da sie sich beide auf dieselben Daten beziehen. Ein Struct ist ein Werttyp. Wenn ein Struct erstellt wird, hält die Variable, der das Struct zugewiesen ist, die tatsächlichen Daten des Structs. Wenn das Struct einer neuen Variable zugewiesen wird, wird es kopiert. Die neue Variable und die originale Variable enthalten daher zwei separate Kopien derselben Daten. Änderungen an einer Kopie haben keine Auswirkungen auf die andere Kopie. Im Allgemeinen werden Klassen verwendet, um komplexeres Verhalten oder Daten zu modellieren, die nach der Erstellung eines Klassenobjekts geändert werden sollen. Structs eignen sich am besten für kleine Datenstrukturen, die hauptsächlich Daten enthalten, die nach der Erstellung des Structs nicht geändert werden sollen.

Klassen und Structs (C#-Programmierhandbuch)

0 Stimmen

Strukturen sind auch sehr gut in Fällen, in denen es notwendig ist, ein paar verwandte, aber unabhängige Variablen mit Klebeband zusammenzubinden (z.B. die Koordinaten eines Punktes). Die MSDN-Richtlinien sind vernünftig, wenn man versucht, Strukturen zu erstellen, die sich wie Objekte verhalten, aber weit weniger geeignet, wenn es um das Entwerfen von Aggregaten geht; einige von ihnen sind in letzterem Fall fast genau falsch. Zum Beispiel, je unabhängiger die Variablen sind, die von einem Typ verkapselt werden, desto größer ist der Vorteil, eine Struktur mit freiliegenden Feldern anstelle einer unveränderlichen Klasse zu verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X