it-swarm-eu.dev

Wann sollte ich eine Struktur anstelle einer Klasse verwenden?

MSDN empfiehlt die Verwendung von Strukturen, wenn Sie kompakte Objekte benötigen. Gibt es andere Szenarien, in denen eine Struktur einer Klasse vorgezogen wird?

Einige Leute haben das vielleicht vergessen:

  1. Strukturen können Methoden haben.
  2. Strukturen können nicht vererbt werden.

Ich verstehe die technischen Unterschiede zwischen Strukturen und Klassen. Ich habe einfach kein gutes Gefühl dafür, dass wann eine Struktur verwendet.

295
Esteban Araya

MSDN hat die Antwort: Auswahl zwischen Klassen und Strukturen .

Grundsätzlich erhalten Sie auf dieser Seite eine Checkliste mit 4 Elementen und der Aufforderung, eine Klasse zu verwenden, sofern Ihr Typ nicht alle Kriterien erfüllt.

Definieren Sie keine Struktur, es sei denn, der Typ weist alle folgenden Merkmale auf:

  • Es stellt logischerweise einen einzelnen Wert dar, ähnlich wie bei primitiven Typen (Integer, Double usw.).
  • Die Instanzgröße ist kleiner als 16 Byte.
  • Es ist unveränderlich.
  • Es muss nicht häufig geboxt werden.
293
OwenP

Ich bin überrascht, dass ich bei keiner der vorherigen Antworten dies gelesen habe, was ich für den ausschlaggebenden Aspekt halte:

Ich benutze Strukturen, wenn ich einen Typ ohne Identität haben möchte. Zum Beispiel ein 3D-Punkt:

public struct ThreeDimensionalPoint
{
    public readonly int X, Y, Z;
    public ThreeDimensionalPoint(int x, int y, int z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public override string ToString()
    {
        return "(X=" + this.X + ", Y=" + this.Y + ", Z=" + this.Z + ")";
    }

    public override int GetHashCode()
    {
        return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is ThreeDimensionalPoint))
            return false;
        ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj;
        return this == other;
    }

    public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z;
    }

    public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return !(p1 == p2);
    }
}

Wenn Sie zwei Instanzen dieser Struktur haben, ist es Ihnen egal, ob es sich um ein oder zwei einzelne Daten im Speicher handelt. Sie interessieren sich nur für die Werte, die sie enthalten.

53
Andrei Rînea

Bill Wagner hat ein Kapitel darüber in seinem Buch "effective c #" ( http://www.Amazon.com/Effective-Specific-Ways-Improve-Your/dp/032124566 ). Er schließt mit dem folgenden Prinzip:

  1. Ist die Hauptverantwortung des Typs Datenspeicherung?
  2. Wird die öffentliche Schnittstelle vollständig durch Eigenschaften definiert, die auf die Datenelemente zugreifen oder diese ändern?
  3. Sind Sie sicher, dass Ihr Typ niemals Unterklassen haben wird?
  4. Sind Sie sicher, dass Ihr Typ niemals polymorph behandelt wird?

Wenn Sie alle 4 Fragen mit "Ja" beantworten, verwenden Sie eine Struktur. Verwenden Sie andernfalls eine Klasse.

27
Bart Gijssens

Verwenden Sie eine Struktur, wenn Sie eine Werttypsemantik anstelle eines Referenztyps wünschen. Strukturen sind wertkopiert, seien Sie also vorsichtig!

Siehe auch vorherige Fragen, z.

Was ist der Unterschied zwischen struct und class in .NET?

15
Simon Steele

Ich würde structs verwenden, wenn:

  1. ein Objekt darf nur gelesen werden (jedes Mal, wenn Sie eine Struktur übergeben/zuweisen, wird sie kopiert). Nur-Lese-Objekte eignen sich hervorragend für die Multithread-Verarbeitung, da sie in den meisten Fällen nicht gesperrt werden müssen.

  2. ein Objekt ist klein und kurzlebig. In einem solchen Fall besteht eine gute Chance, dass das Objekt auf dem Stapel zugeordnet wird, was wesentlich effizienter ist, als es auf dem verwalteten Heap abzulegen. Außerdem wird der vom Objekt zugewiesene Speicher freigegeben, sobald er außerhalb seines Gültigkeitsbereichs liegt. Mit anderen Worten, es ist weniger Arbeit für den Garbage Collector und der Speicher wird effizienter genutzt.

11
Pawel Pabich

Verwenden Sie eine Klasse, wenn:

  • Ihre Identität ist wichtig. Strukturen werden implizit kopiert, wenn sie als Wert an eine Methode übergeben werden.
  • Es wird einen großen Speicherbedarf haben.
  • Seine Felder benötigen Initialisierer.
  • Sie müssen von einer Basisklasse erben.
  • Sie brauchen polymorphes Verhalten;

Verwenden Sie eine Struktur, wenn:

  • Es verhält sich wie ein primitiver Typ (int, long, byte usw.).
  • Es muss einen geringen Speicherbedarf haben.
  • Sie rufen eine P/Invoke-Methode auf, für die eine Struktur als Wert übergeben werden muss.
  • Sie müssen die Auswirkungen der Garbage Collection auf die Anwendungsleistung reduzieren.
  • Seine Felder müssen nur auf ihre Standardwerte initialisiert werden. Dieser Wert wäre null für numerische Typen, false für Boolesche Typen und null für Referenztypen.
    • Beachten Sie, dass Strukturen in C # 6.0 einen Standardkonstruktor haben können, mit dem die Felder der Struktur mit nicht standardmäßigen Werten initialisiert werden können.
  • Sie müssen nicht von einer Basisklasse erben (mit Ausnahme von ValueType, von dem alle Strukturen erben).
  • Sie brauchen kein polymorphes Verhalten.

Ich habe immer eine Struktur verwendet, wenn ich ein paar Werte zusammenfassen wollte, um Dinge von einem Methodenaufruf zurückzugeben, aber ich brauche sie für nichts mehr zu verwenden, nachdem ich diese Werte gelesen habe. Nur um die Dinge sauber zu halten. Ich neige dazu, Dinge in einer Struktur als "Wegwerf" und Dinge in einer Klasse als nützlicher und "funktionaler" zu betrachten.

5
Ryan Skarin

Wenn eine Entität unveränderlich sein wird, hängt die Frage, ob eine Struktur oder eine Klasse verwendet wird, im Allgemeinen eher von der Leistung als von der Semantik ab. Auf einem 32/64-Bit-System sind für die Speicherung von Klassenreferenzen 4/8 Byte erforderlich, unabhängig von der Informationsmenge in der Klasse. Das Kopieren einer Klassenreferenz erfordert das Kopieren von 4/8 Bytes. Auf der anderen Seite jeder deutlich Die Klasseninstanz hat zusätzlich zu den darin enthaltenen Informationen und den Speicherkosten der Verweise auf diese 8/16 Byte Overhead. Angenommen, man möchte ein Array von 500 Entitäten, die jeweils vier 32-Bit-Ganzzahlen enthalten. Wenn es sich bei der Entität um einen Strukturtyp handelt, benötigt das Array 8.000 Byte, unabhängig davon, ob alle 500 Entitäten identisch, alle unterschiedlich oder irgendwo dazwischen sind. Wenn es sich bei der Entität um einen Klassentyp handelt, benötigt das Array mit 500 Referenzen 4.000 Bytes. Wenn diese Verweise alle auf unterschiedliche Objekte verweisen, benötigen die Objekte jeweils zusätzliche 24 Bytes (12.000 Bytes für alle 500 Bytes), also insgesamt 16.000 Bytes - das Doppelte der Speicherkosten eines Strukturtyps. Wenn der Code hingegen eine Objektinstanz erstellt und dann eine Referenz auf alle 500 Array-Slots kopiert hat, betragen die Gesamtkosten für diese Instanz 24 Byte und für das Array 4.000 Byte - insgesamt 4.024 Byte. Eine große Ersparnis. Nur wenige Situationen würden so gut funktionieren wie die letzte, aber in einigen Fällen ist es möglich, einige Verweise auf genügend Array-Slots zu kopieren, damit sich eine solche Freigabe lohnt.

Wenn die Entität veränderlich sein soll, ist die Frage, ob eine Klasse oder eine Struktur verwendet werden soll, in gewisser Weise einfacher. Angenommen, "Thing" ist entweder eine Struktur oder eine Klasse mit einem ganzzahligen Feld namens x, und man führt den folgenden Code aus:

 Sache t1, t2; 
 ... 
 T2 = t1; 
 T2.x = 5; 

Möchte man, dass die letztere Aussage t1.x beeinflusst?

Wenn Thing ein Klassentyp ist, sind t1 und t2 äquivalent, was bedeutet, dass t1.x und t2.x ebenfalls äquivalent sind. Somit wirkt sich die zweite Anweisung auf t1.x aus. Wenn Thing ein Strukturtyp ist, sind t1 und t2 unterschiedliche Instanzen, was bedeutet, dass sich t1.x und t2.x auf unterschiedliche Ganzzahlen beziehen. Die zweite Anweisung wirkt sich daher nicht auf t1.x aus.

Mutable Strukturen und mutable Klassen haben grundsätzlich unterschiedliche Verhaltensweisen, obwohl .net einige Macken im Umgang mit Strukturmutationen hat. Wenn Sie ein Verhalten mit Wertetypen wünschen (dh "t2 = t1" kopiert die Daten von t1 nach t2, wobei t1 und t2 unterschiedliche Instanzen bleiben) und wenn Sie mit den Macken im Umgang mit Wertetypen in .net leben können, verwenden Sie eine Struktur. Verwenden Sie eine Klasse und murmeln Sie, wenn Sie eine Semantik vom Typ Wert möchten, die Macken von .net jedoch zu einer fehlerhaften Semantik vom Typ Wert in Ihrer Anwendung führen.

4
supercat

Dazu die hervorragenden Antworten oben:

Strukturen sind Werttypen.

Sie können niemals auf Nothing gesetzt werden.

Wenn Sie eine Struktur auf "Nothing" setzen, werden alle Wertetypen auf ihre Standardwerte zurückgesetzt.

3

wenn Sie kein wirkliches Verhalten benötigen, aber mehr Struktur als ein einfaches Array oder Wörterbuch benötigen.

Follow-up So stelle ich mir Strukturen im Allgemeinen vor. Ich weiß, dass sie Methoden haben können, aber ich mag es, diesen allgemeinen mentalen Unterschied beizubehalten.

2
Jim Deville

Wie @Simon bereits sagte, bieten Strukturen eine Semantik vom Typ "Wert". Wenn Sie also ein ähnliches Verhalten wie ein eingebauter Datentyp benötigen, verwenden Sie eine Struktur. Da Strukturen als Kopie übergeben werden, sollten Sie sicherstellen, dass sie klein sind (etwa 16 Byte).

2
Scott Dorman

Strukturen befinden sich auf dem Stapel, nicht auf dem Heap, daher sind sie threadsicher und sollten bei der Implementierung des Übertragungsobjektmusters verwendet werden. Sie möchten niemals Objekte auf dem Heap verwenden, sie sind flüchtig. In diesem Fall möchten Sie den Aufrufstapel verwenden. Dies ist ein grundlegender Fall für die Verwendung einer Struktur.

1
Jack

Hmm ...

Ich würde Garbage Collection nicht als Argument für/gegen die Verwendung von Strukturen gegen Klassen verwenden. Der verwaltete Heap funktioniert ähnlich wie ein Stapel: Wenn Sie ein Objekt erstellen, wird es ganz oben auf dem Heap platziert. Dies ist fast so schnell wie die Zuweisung auf dem Stapel. Wenn ein Objekt nur von kurzer Dauer ist und einen GC-Zyklus nicht überlebt, ist die Freigabe kostenlos, da der GC nur mit Speicher funktioniert, auf den noch zugegriffen werden kann. (Durchsuchen Sie MSDN, es gibt eine Reihe von Artikeln zur .NET-Speicherverwaltung. Ich bin einfach zu faul, um nach ihnen zu suchen.).

Die meiste Zeit benutze ich eine Struktur, und ich mache mir einen Kick, weil ich später herausfinde, dass die Referenzsemantik die Dinge ein bisschen einfacher gemacht hätte.

Wie auch immer, diese vier Punkte in dem oben geposteten MSDN-Artikel scheinen eine gute Richtlinie zu sein.

1
KG