338 Stimmen

Wann sollte ich eine Struktur anstelle einer Klasse verwenden?

MSDN sagt, dass Sie structs verwenden sollten, wenn Sie leichtgewichtige Objekte benötigen. Gibt es noch andere Szenarien, in denen eine Struktur einer Klasse vorzuziehen ist?

Manche Leute haben das vielleicht vergessen:

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

Ich verstehe die technischen Unterschiede zwischen Structs und Klassen, ich habe nur kein gutes Gefühl für wenn um eine Struktur zu verwenden.

14voto

Pawel Pabich Punkte 2284

Ich würde structs verwenden, wenn:

  1. ein Objekt soll nur gelesen werden (jedes Mal, wenn Sie eine Struktur übergeben/zuweisen, wird sie kopiert). Nur-Lese-Objekte sind großartig, wenn es um Multithreading-Verarbeitung kommt, da sie in den meisten Fällen keine Sperrung erfordern.

  2. ein Objekt ist klein und kurzlebig. In einem solchen Fall ist die Wahrscheinlichkeit groß, dass das Objekt auf dem Stack alloziert wird, was viel effizienter ist als es auf den verwalteten Heap zu legen. Außerdem wird der vom Objekt zugewiesene Speicher wieder freigegeben, sobald es seinen Bereich verlässt. Mit anderen Worten: Es ist weniger Arbeit für den Garbage Collector und der Speicher wird effizienter genutzt.

10voto

Eduardas Šlutas Punkte 139

Es ist ein altes Thema, aber ich wollte einen einfachen Benchmark-Test durchführen.

Ich habe zwei .cs-Dateien erstellt:

public class TestClass
{
    public long ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

und

public struct TestStruct
{
    public long ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Benchmark ausführen:

  • 1 TestClass erstellen
  • 1 TestStruct erstellen
  • 100 TestClass erstellen
  • 100 TestStruct erstellen
  • 10000 TestClass erstellen
  • 10000 TestStruct erstellen

Ergebnisse:

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.101
[Host]     : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT  [AttachedDebugger]
DefaultJob : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT

|         Method |           Mean |         Error |        StdDev |     Ratio | RatioSD | Rank |    Gen 0 | Gen 1 | Gen 2 | Allocated |
|--------------- |---------------:|--------------:|--------------:|----------:|--------:|-----:|---------:|------:|------:|----------:|

|      UseStruct |      0.0000 ns |     0.0000 ns |     0.0000 ns |     0.000 |    0.00 |    1 |        - |     - |     - |         - |
|       UseClass |      8.1425 ns |     0.1873 ns |     0.1839 ns |     1.000 |    0.00 |    2 |   0.0127 |     - |     - |      40 B |
|   Use100Struct |     36.9359 ns |     0.4026 ns |     0.3569 ns |     4.548 |    0.12 |    3 |        - |     - |     - |         - |
|    Use100Class |    759.3495 ns |    14.8029 ns |    17.0471 ns |    93.144 |    3.24 |    4 |   1.2751 |     - |     - |    4000 B |
| Use10000Struct |  3,002.1976 ns |    25.4853 ns |    22.5920 ns |   369.664 |    8.91 |    5 |        - |     - |     - |         - |
|  Use10000Class | 76,529.2751 ns | 1,570.9425 ns | 2,667.5795 ns | 9,440.182 |  346.76 |    6 | 127.4414 |     - |     - |  400000 B |

6voto

Ryan Skarin Punkte 3067

Ich habe immer eine Struktur verwendet, wenn ich einige Werte zusammenfassen wollte, um Dinge aus einem Methodenaufruf zurückzugeben, aber ich brauche sie nicht mehr für irgendetwas zu verwenden, nachdem ich diese Werte gelesen habe. Nur so kann ich die Dinge sauber halten. Ich neige dazu, Dinge in einer Struktur als "wegwerfbar" und Dinge in einer Klasse als nützlicher und "funktional" zu betrachten.

6voto

supercat Punkte 72939

Wenn eine Entität unveränderlich sein soll, ist die Frage, ob eine Struktur oder eine Klasse verwendet werden soll, im Allgemeinen eher eine Frage der Leistung als der Semantik. Auf einem 32/64-Bit-System benötigen Klassenreferenzen 4/8 Bytes zum Speichern, unabhängig von der Menge der Informationen in der Klasse; das Kopieren einer Klassenreferenz erfordert das Kopieren von 4/8 Bytes. Auf der anderen Seite wird jede deutlich Klasseninstanz hat 8/16 Bytes Overhead zusätzlich zu den Informationen, die sie enthält, und den Speicherkosten der Verweise auf sie. 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. Handelt es sich bei der Entität um einen Klassentyp, benötigt das Array mit 500 Referenzen 4.000 Bytes. Wenn diese Verweise alle auf unterschiedliche Objekte verweisen, würden die Objekte jeweils weitere 24 Byte benötigen (12.000 Byte für alle 500), insgesamt also 16.000 Byte - das Doppelte der Speicherkosten eines Strukturtyps. Würde der Code hingegen eine Objektinstanz erstellen und dann einen Verweis auf alle 500 Array-Slots kopieren, so würden die Gesamtkosten 24 Byte für diese Instanz und 4.000 Byte für das Array betragen - insgesamt 4.024 Byte. Eine große Ersparnis. Nur wenige Situationen würden so gut funktionieren wie die letzte, aber in einigen Fällen kann es möglich sein, einige Referenzen auf genügend Array-Slots zu kopieren, damit sich eine solche gemeinsame Nutzung lohnt.

Wenn die Entität veränderbar sein soll, ist die Frage, ob eine Klasse oder eine Struktur verwendet werden soll, in gewisser Weise einfacher. Nehmen wir an, "Thing" ist entweder ein struct oder eine Klasse, die ein Integer-Feld namens x hat, und man macht den folgenden Code:

  Thing t1,t2;
  ...
  t2 = t1;
  t2.x = 5;

Soll sich die letztgenannte Aussage auf t1.x auswirken?

Wenn Thing ein Klassentyp ist, sind t1 und t2 äquivalent, d. h. t1.x und t2.x sind ebenfalls äquivalent. Wenn Thing ein Strukturtyp ist, sind t1 und t2 unterschiedliche Instanzen, d.h. t1.x und t2.x beziehen sich auf unterschiedliche Ganzzahlen. Daher wirkt sich die zweite Anweisung nicht auf t1.x aus.

Veränderliche Strukturen und veränderliche Klassen haben ein grundsätzlich unterschiedliches Verhalten, obwohl .net einige Macken in der Behandlung von Strukturmutationen hat. Wenn man ein wertartiges Verhalten wünscht (was bedeutet, dass "t2=t1" die Daten von t1 nach t2 kopiert, während t1 und t2 als unterschiedliche Instanzen bestehen bleiben) und wenn man mit den Macken von .net bei der Behandlung von Werttypen leben kann, sollte man eine Struktur verwenden. Wenn man Werttyp-Semantik will, aber die Macken von .net würden dazu führen, dass die Werttyp-Semantik in der eigenen Anwendung nicht funktioniert, sollte man eine Klasse verwenden und mumble.

4voto

George Filippakos Punkte 15531

Zusätzlich zu den hervorragenden Antworten oben:

Strukturen sind Werttypen.

Sie können niemals eingestellt werden auf Nichts .

Wenn Sie eine Struktur = Nothing setzen, werden alle ihre Wertetypen auf ihre Standardwerte gesetzt.

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