Was ist der Unterschied zwischen Struktur und Klasse in .NET?


Antworten:


1057

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.

43
Im Interesse der vollständigen Vollständigkeit sollte ich erwähnen, dass Eric Lippert gesagt hat, dass der Stack ein Implementierungsdetail ist. Wenn ich oben den Stack erwähne, denken Sie an Erics Post (s).
Lasse V. Karlsen

2
Gilt das alles auch für C ++?
Koray Tugay

9
Ein weiterer entscheidender Unterschied ist die Nutzung. Aus MSDN: "Strukturen werden normalerweise verwendet, um eine kleine Gruppe verwandter Variablen, wie z. B. Koordinaten von Rechtecken, zu kapseln. Strukturen können auch Konstruktoren, Konstanten, Felder, Methoden, Eigenschaften, Indexer, Operatoren, Ereignisse und verschachtelte Typen enthalten, wenn auch mehrere Mitglieder sind erforderlich, Sie sollten stattdessen in Betracht ziehen, Ihren Typ zu einer Klasse zu machen. "
Thewpfguy

4
@KorayTugay Nein, ist es nicht.
ZoomIn

9
@KorayTugay in C ++ Struktur und Klasse sind absolut gleichwertig, mit Ausnahme der einen Sache - Standardzugriffsbeschränkung (Klasse hat standardmäßig privat, Struktur hat öffentlich)
berkus

207

Eine kurze Zusammenfassung von jedem:

Nur Klassen:

  • Kann die Vererbung unterstützen
  • Sind Referenztypen (Zeigertypen)
  • Die Referenz kann null sein
  • Haben Sie Speicher-Overhead pro neuer Instanz

Nur Strukturen:

  • Vererbung kann nicht unterstützt werden
  • Sind Werttypen
  • Werden als Wert übergeben (wie ganze Zahlen)
  • Kann keine Nullreferenz haben (es sei denn, Nullable wird verwendet)
  • Kein Speicher-Overhead pro neuer Instanz - es sei denn, "Boxed"

Sowohl Klassen als auch Strukturen:

  • Werden zusammengesetzte Datentypen normalerweise verwendet, um einige Variablen zu enthalten, die eine logische Beziehung haben?
  • Kann Methoden und Ereignisse enthalten
  • Kann Schnittstellen unterstützen

16
Es gibt einige Teile dieser Antwort, die nicht ganz richtig sind. Klassen werden nicht immer auf dem Heap gespeichert, und Strukturen werden nicht immer auf dem Stapel abgelegt. Aktuelle Ausnahmen sind Strukturfelder in einer Klasse, erfasste Variablen in anonymen Methoden und Lambda-Ausdrücken, Iteratorblöcke und die bereits erwähnten Box-Werte. Die Zuordnung von Stapel zu Heap ist jedoch ein Implementierungsdetail und kann sich ändern. Eric Lippart diskutiert dies hier . Ich habe abgestimmt, werde es aber gerne entfernen, wenn Sie aktualisieren.
Simon P Stevens

1
struct unterstützt keine Vererbung von anderen Strukturen / Klassen, aber Sie können eine Schnittstelle in einer struct implementieren.
Thewpfguy

2
Möglicherweise möchten Sie klarstellen, was Sie meinen, wenn Sie behaupten, dass Strukturen "keinen Speicheraufwand pro neuer Instanz haben" . Meine erste Interpretation war, dass Sie - offensichtlich absurd - behaupteten, dass Strukturen keinen Speicher verwenden. Dann dachte ich, dass Sie vielleicht versuchen zu sagen, dass eine Struktur im Gegensatz zu einer Klasse genau so viel Speicher benötigt wie die Summe ihrer Mitgliedsfelder und nicht mehr. Aber dann habe ich gegoogelt c# struct memory overheadund diese Antwort von Hans Passant gefunden, die besagt, dass nein, das ist auch nicht der Fall. Also, was meinst du?
Mark Amery

4
@MarkAmery Ich hatte die gleiche anfängliche Reaktion wie Sie auf den Ausdruck "kein Speicher-Overhead", aber ich denke, dass sich das OP auf die Tatsache bezieht, dass Instanzen von classverwaltetem Speicher sind (vom Garbage Collector verarbeitet), Instanzen von structnicht .
Hutch

1
"Struktur wird als Wert übergeben (wie Ganzzahlen)" ist falsch: Alle Variablen werden als Wert übergeben, auch der Referenztyp. Wenn Sie eine Variable als Referenz übergeben möchten, müssen Sie das Schlüsselwort "ref" verwenden. jonskeet.uk/csharp/parameters.html#ref
Marco Staffoli

41

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:

struct MyStruct 
{
    string MyProperty { get; set; }
}

void ChangeMyStruct(MyStruct input) 
{ 
   input.MyProperty = "new value";
}

...

// Create value type
MyStruct testStruct = new MyStruct { MyProperty = "initial value" }; 

ChangeMyStruct(testStruct);

// Value of testStruct.MyProperty is still "initial value"
// - the method changed a new copy of the structure.

Für eine Klasse wäre das anders

class MyClass 
{
    string MyProperty { get; set; }
}

void ChangeMyClass(MyClass input) 
{ 
   input.MyProperty = "new value";
}

...

// Create reference type
MyClass testClass = new MyClass { MyProperty = "initial value" };

ChangeMyClass(testClass);

// Value of testClass.MyProperty is now "new value" 
// - the method changed the instance passed.

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'.


@ T.Todua Ja, es gibt oben bessere Antworten, die ich gewählt und als Antwort ausgewählt habe, nachdem ich diese bereitgestellt habe - dies ist aus der frühen Beta von SO, als wir noch die Regeln herausgefunden haben.
Keith

1
Ich weiß nicht, ob Sie mich richtig verstanden haben. Ich habe Ihre Antwort wirklich positiv bewertet / akzeptiert (im Gegensatz zu den obigen Antworten), weil Ihre gute Beispiele hatten (nicht nur theoretische Erklärung, im Gegensatz zu der obigen Antwort, die nur theoretische Erklärungen ohne Beispiele hatte ).
T.Todua

24

Unterschied zwischen Strukturen und Klassen:

  • Strukturen sind Werttypen, während Klassen Referenztypen sind .
  • Strukturen werden auf dem Stapel gespeichert, während Klassen auf dem Heap gespeichert werden .
  • Werttypen speichern ihren Wert im Speicher, in dem sie deklariert sind, aber der Referenztyp enthält einen Verweis auf einen Objektspeicher.
  • Werttypen, die unmittelbar nach dem Verlust des Bereichs zerstört werden, während der Referenztyp nur die Variable zerstört, nachdem der Bereich verloren gegangen ist. Das Objekt wird später vom Garbage Collector zerstört.
  • Wenn Sie eine Struktur in eine andere Struktur kopieren, wird eine neue Kopie dieser Struktur erstellt, die von einer Struktur geändert wird. Dies wirkt sich nicht auf den Wert der anderen Struktur aus.
  • Wenn Sie eine Klasse in eine andere Klasse kopieren, wird nur die Referenzvariable kopiert.
  • Beide Referenzvariablen zeigen auf dasselbe Objekt auf dem Heap. Die Änderung einer Variablen wirkt sich auf die andere Referenzvariable aus.
  • Strukturen können keine Destruktoren haben , aber Klassen können Destruktoren haben.
  • Strukturen können keine expliziten parameterlosen Konstruktoren haben, während eine Klasse kann, dass Strukturen keine Vererbung unterstützen, Klassen jedoch. Beide unterstützen die Vererbung von einer Schnittstelle.
  • Strukturen sind versiegelt .

21

Aus der Wahl von Microsoft zwischen Klasse und Struktur ...

Als Faustregel gilt, dass die meisten Typen in einem Framework Klassen sein sollten. Es gibt jedoch Situationen, in denen die Eigenschaften eines Werttyps die Verwendung von Strukturen angemessener machen.

Betrachten Sie eine Struktur anstelle einer Klasse:

  • Wenn Instanzen des Typs klein und häufig von kurzer Dauer sind oder häufig in andere Objekte eingebettet sind.

X Vermeiden Sie eine Struktur, es sei denn, der Typ weist alle folgenden Merkmale auf:

  • Es stellt logischerweise einen einzelnen Wert dar, ähnlich wie primitive Typen (int, double usw.).
  • Es hat eine Instanzgröße unter 16 Bytes.
  • Es ist unveränderlich. (kann nicht geändert werden)
  • Es muss nicht häufig verpackt werden.

19

Zusätzlich zu allen in den anderen Antworten beschriebenen Unterschieden:

  1. Strukturen können keinen expliziten parameterlosen Konstruktor haben, während eine Klasse dies kann
  2. Strukturen können keine Destruktoren haben , während eine Klasse dies kann
  3. Strukturen können nicht von einer anderen Struktur oder Klasse erben, während eine Klasse von einer anderen Klasse erben kann. (Sowohl Strukturen als auch Klassen können über eine Schnittstelle implementiert werden.)

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 # .


4
Viel wichtiger als die Tatsache, dass .net-Sprachen es einer Struktur im Allgemeinen nicht erlauben, einen parameterlosen Konstruktor zu definieren (die Entscheidung, ob sie dies zulässt oder nicht, trifft der Sprachcompiler), ist die Tatsache, dass Strukturen entstehen und verfügbar gemacht werden können nach außen, ohne dass ein Konstruktor ausgeführt wurde (selbst wenn ein parameterloser Konstruktor definiert ist). Der Grund, warum .net-Sprachen im Allgemeinen parameterlose Konstruktoren für Strukturen verbieten, besteht darin, die Verwirrung zu vermeiden, die entstehen würde, wenn solche Konstruktoren manchmal ausgeführt würden und manchmal nicht.
Supercat

15

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.


11
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                        |                                                Struct                                                |                                               Class                                               |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| Type                   | Value-type                                                                                           | Reference-type                                                                                    |
| Where                  | On stack / Inline in containing type                                                                 | On Heap                                                                                           |
| Deallocation           | Stack unwinds / containing type gets deallocated                                                     | Garbage Collected                                                                                 |
| Arrays                 | Inline, elements are the actual instances of the value type                                          | Out of line, elements are just references to instances of the reference type residing on the heap |
| Aldel Cost             | Cheap allocation-deallocation                                                                        | Expensive allocation-deallocation                                                                 |
| Memory usage           | Boxed when cast to a reference type or one of the interfaces they implement,                         | No boxing-unboxing                                                                                |
|                        | Unboxed when cast back to value type                                                                 |                                                                                                   |
|                        | (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) |                                                                                                   |
| Assignments            | Copy entire data                                                                                     | Copy the reference                                                                                |
| Change to an instance  | Does not affect any of its copies                                                                    | Affect all references pointing to the instance                                                    |
| Mutability             | Should be immutable                                                                                  | Mutable                                                                                           |
| Population             | In some situations                                                                                   | Majority of types in a framework should be classes                                                |
| Lifetime               | Short-lived                                                                                          | Long-lived                                                                                        |
| Destructor             | Cannot have                                                                                          | Can have                                                                                          |
| Inheritance            | Only from an interface                                                                               | Full support                                                                                      |
| Polymorphism           | No                                                                                                   | Yes                                                                                               |
| Sealed                 | Yes                                                                                                  | When have sealed keyword                                                                          |
| Constructor            | Can not have explicit parameterless constructors                                                     | Any constructor                                                                                   |
| Null-assignments       | When marked with nullable question mark                                                              | Yes (+ When marked with nullable question mark in C# 8+)                                          |
| Abstract               | No                                                                                                   | When have abstract keyword                                                                        |
| Member Access Modifiers| public, private, internal                                                                            | public, protected, internal, protected internal, private protected                                |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+

1
Das ist eigentlich ganz großartig: zusammengefasst und informativ. Bitte denken Sie nur daran, Ihre Antwort mindestens einmal Korrektur zu lesen - Sie haben die Struktur- und Klassenerklärungen in einigen Zeilen ausgetauscht, außerdem gibt es einige Tippfehler.
Robert Kopeć

1
@ensisNoctis Entschuldigung für diese Fehler und danke für die Bearbeitung. Ich sollte meine Antworten read
0xaryan

8

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.


5
Zu "Alle Strukturmitglieder sind öffentlich": Wenn ich mich nicht irre, ist das falsch. "Die Zugriffsebene für Klassenmitglieder und Strukturmitglieder, einschließlich verschachtelter Klassen und Strukturen, ist standardmäßig privat." msdn.microsoft.com/en-us/library/ms173121.aspx
Nate Cook

8

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.

  • Bei einer Struktur enthält das Array die Instanz der Struktur
  • Bei einer Klasse enthält das Array einen Zeiger auf eine Instanz der Klasse an einer anderen Stelle im Speicher

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.

    private struct PerformanceStruct
    {
        public int i1;
        public int i2;
    }

    private class PerformanceClass
    {
        public int i1;
        public int i2;
    }

    private static void DoTest()
    {
        var structArray = new PerformanceStruct[100000000];
        var classArray = new PerformanceClass[structArray.Length];

        for (var i = 0; i < structArray.Length; i++)
        {
            structArray[i] = new PerformanceStruct();
            classArray[i] = new PerformanceClass();
        }

        long total = 0;
        var sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < structArray.Length; i++)
        {
            total += structArray[i].i1 + structArray[i].i2;
        }

        sw.Stop();
        Console.WriteLine($"Struct Time: {sw.ElapsedMilliseconds}");
        sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < classArray.Length; i++)
        {
            total += classArray[i].i1 + classArray[i].i2;
        }

        Console.WriteLine($"Class Time: {sw.ElapsedMilliseconds}");
    }

-1; "Strukturen sind Werttypen, sie speichern also einen Wert, Klassen sind Referenztypen, sie verweisen also auf eine Klasse." ist unklar und es ist unwahrscheinlich, dass es für jemanden Sinn macht, der es aus den anderen Antworten hier noch nicht verstanden hat. "Bei einer Klasse enthält die enthaltende Klasse nur einen Zeiger auf die neue Klasse in einem anderen Speicherbereich." verwechselt Klassen mit Klasseninstanzen.
Mark Amery

@ MarkAmery Ich habe versucht, etwas zu klären. Der Punkt, den ich wirklich ansprechen wollte, war der Unterschied in der Art und Weise, wie Arrays mit Wert- und Referenztypen arbeiten, und die Auswirkungen, die dies auf die Leistung hat. Ich habe nicht versucht, den Wert und die Referenztypen erneut zu erklären, da dies in vielen anderen Antworten geschieht.
Will Calderwood

7

Um es zu vervollständigen, gibt es einen weiteren Unterschied bei der Verwendung der EqualsMethode, die von allen Klassen und Strukturen geerbt wird.

Nehmen wir an, wir haben eine Klasse und eine Struktur:

class A{
  public int a, b;
}
struct B{
  public int a, b;
}

und in der Hauptmethode haben wir 4 Objekte.

static void Main{
  A c1 = new A(), c2 = new A();
  c1.a = c1.b = c2.a = c2.b = 1;
  B s1 = new B(), s2 = new B();
  s1.a = s1.b = s2.a = s2.b = 1;
}

Dann:

s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false

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.


6

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.


4

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:

  • Sind Werttypen und erfordern keine Heap-Zuordnung.
  • Die Speicherzuordnung ist unterschiedlich und wird im Stapel gespeichert
  • Nützlich für kleine Datenstrukturen
  • Wenn wir den Wert an die Methode übergeben, übergeben wir die gesamte Datenstruktur und alles wird an den Stapel übergeben.
  • Der Konstruktor gibt einfach den Strukturwert selbst zurück (normalerweise an einem temporären Speicherort auf dem Stapel), und dieser Wert wird dann nach Bedarf kopiert
  • Die Variablen haben jeweils eine eigene Kopie der Daten, und es ist nicht möglich, dass Operationen an einer die andere beeinflussen.
  • Benutzerdefinierte Vererbung wird nicht unterstützt, und sie erben implizit vom Typobjekt

Klasse:

  • Referenztypwert
  • Im Haufen gespeichert
  • Speichern Sie einen Verweis auf ein dynamisch zugewiesenes Objekt
  • Konstruktoren werden mit dem neuen Operator aufgerufen, aber das reserviert keinen Speicher auf dem Heap
  • Mehrere Variablen können auf dasselbe Objekt verweisen
  • Es ist möglich, dass Operationen an einer Variablen das Objekt beeinflussen, auf das die andere Variable verweist

Codebeispiel

    static void Main(string[] args)
    {
        //Struct
        myStruct objStruct = new myStruct();
        objStruct.x = 10;
        Console.WriteLine("Initial value of Struct Object is: " + objStruct.x);
        Console.WriteLine();
        methodStruct(objStruct);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Struct Object is: " + objStruct.x);
        Console.WriteLine();

        //Class
        myClass objClass = new myClass(10);
        Console.WriteLine("Initial value of Class Object is: " + objClass.x);
        Console.WriteLine();
        methodClass(objClass);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Class Object is: " + objClass.x);
        Console.Read();
    }
    static void methodStruct(myStruct newStruct)
    {
        newStruct.x = 20;
        Console.WriteLine("Inside Struct Method");
        Console.WriteLine("Inside Method value of Struct Object is: " + newStruct.x);
    }
    static void methodClass(myClass newClass)
    {
        newClass.x = 20;
        Console.WriteLine("Inside Class Method");
        Console.WriteLine("Inside Method value of Class Object is: " + newClass.x);
    }
    public struct myStruct
    {
        public int x;
        public myStruct(int xCons)
        {
            this.x = xCons;
        }
    }
    public class myClass
    {
        public int x;
        public myClass(int xCons)
        {
            this.x = xCons;
        }
    }

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.


4
  1. 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.

  2. 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.

  3. Eine Struktur verfügt immer über einen integrierten öffentlichen Standardkonstruktor.

    class DefaultConstructor
    {
        static void Eg()
        {
            Direct     yes = new   Direct(); // Always compiles OK
            InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
            //...
        }
    }

    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.

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
  4. 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.

    struct Direct
    {
        ~Direct() {} // Compile-time error
    }
    class InDirect
    {
        ~InDirect() {} // Compiles OK
    }
    
    And the CIL for ~Indirect() looks like this:
    
    .method family hidebysig virtual instance void
            Finalize() cil managed
    {
      // ...
    } // end of method Indirect::Finalize
  5. 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.


Unter welchen Umständen würde man ein Ereignis mit einer Struktur verwenden? Ich kann mir vorstellen, dass ein sehr sorgfältig geschriebenes Programm Ereignisse mit einer Struktur auf eine Weise verwenden könnte, die funktionieren würde, aber nur, wenn die Struktur nie kopiert oder als Wert übergeben wurde. In diesem Fall könnte es auch eine Klasse sein.
Supercat

@supercat Ja, ein nicht statisches Ereignis in einer Struktur wäre sehr seltsam und nur für veränderbare Strukturen nützlich. Das Ereignis selbst (wenn es sich um ein "feldähnliches" Ereignis handelt) verwandelt die Struktur in eine veränderbare "Kategorie und führt auch ein Feld des Referenztyps in die Struktur ein. Nicht statische Ereignisse in Strukturen müssen böse sein.
Jeppe Stig Nielsen

@JeppeStigNielsen: Das einzige Muster, das ich sehen konnte, wo es für eine Struktur sinnvoll wäre, ein Ereignis zu haben, wäre, wenn der Zweck der Struktur darin bestand, einen unveränderlichen Verweis auf ein Klassenobjekt zu enthalten, für das sie sich als Proxy verhält. Auto-Events wären in einem solchen Szenario jedoch völlig nutzlos. Stattdessen müssten Ereignisse zum Abonnieren und Abbestellen an die Klasse hinter der Struktur weitergeleitet werden. Ich wünschte, .NET hätte (oder würde es ermöglichen, einen 'Cache-Box'-Strukturtyp mit einem anfangs null versteckten Feld vom Typ zu definieren Object, der einen Verweis auf eine Box-Kopie der Struktur enthalten würde.
supercat

1
@JeppeStigNielsen: Strukturen übertreffen Klassen in vielen Proxy-Verwendungsszenarien. Das größte Problem bei der Verwendung von Strukturen besteht darin, dass in Fällen, in denen das Boxen notwendig wird, es häufig auf eine innere Schleife verschoben wird. Wenn es eine Möglichkeit gäbe, zu vermeiden, dass Strukturen wiederholt eingerahmt werden , wären sie in viel mehr Nutzungsszenarien besser als Klassen.
Supercat

4

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:

  • Es hat eine Instanzgröße unter 16 Bytes
  • Es stellt logischerweise einen einzelnen Wert dar, ähnlich wie primitive Typen (int, double usw.)
  • Es ist unveränderlich
  • Es muss nicht häufig verpackt werden

3

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

BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT


            Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
  TestStructReturn |  Clr |     Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns |    4 | 0.0127 |      40 B |
   TestClassReturn |  Clr |     Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns |    5 | 0.0229 |      72 B |
 TestStructReturn8 |  Clr |     Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns |    8 | 0.0127 |      40 B |
  TestClassReturn8 |  Clr |     Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns |    6 | 0.0305 |      96 B |
  TestStructReturn | Core |    Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns |    1 | 0.0127 |      40 B |
   TestClassReturn | Core |    Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns |    2 | 0.0229 |      72 B |
 TestStructReturn8 | Core |    Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns |    7 | 0.0127 |      40 B |
  TestClassReturn8 | Core |    Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns |    3 | 0.0305 |      96 B |

Codetest:

using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;

namespace Benchmark
{
    //[Config(typeof(MyManualConfig))]
    [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkStructOrClass
    {
        static TestStruct testStruct = new TestStruct();
        static TestClass testClass = new TestClass();
        static TestStruct8 testStruct8 = new TestStruct8();
        static TestClass8 testClass8 = new TestClass8();
        [Benchmark]
        public void TestStructReturn()
        {
            testStruct.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn()
        {
            testClass.TestMethod();
        }


        [Benchmark]
        public void TestStructReturn8()
        {
            testStruct8.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn8()
        {
            testClass8.TestMethod();
        }

        public class TestStruct
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestClass
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestStruct8
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }

        public class TestClass8
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }
    }
}

2

Strukturen sind der tatsächliche Wert - sie können leer sein, aber niemals null

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.

int? value = null;
value  = 1;

1
Beachten Sie, dass dies nur syntaktischer Zucker ist, der 'Nullable <int> value = null;' lautet.
Erik van Brakel

@ErikvanBrakel Das ist nicht nur syntaktischer Zucker. Die unterschiedlichen Boxregeln bedeuten, (object)(default(int?)) == nulldass Sie mit keinem anderen Werttyp etwas anfangen können, da hier mehr als nur Zucker vor sich geht. Der einzige Zucker ist int?für Nullable<int>.
Jon Hanna

-1; Dies geht nicht auf die Frage ein, was der Unterschied zwischen Strukturen und Klassen ist, und als solches sollte es ein Kommentar zu der Antwort sein, auf die Sie antworten, und keine separate Antwort. (Obwohl vielleicht die Site-Normen im August 2008 anders waren!)
Mark Amery

1

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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.