Frage

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

War es hilfreich?

Lösung

In .NET gibt es zwei Kategorien von Typen: Referenztypen Und Werttypen.

Strukturen sind Werttypen und Klassen sind Referenztypen.

Der allgemeine Unterschied besteht darin, dass a Referenztyp lebt auf dem Haufen, und a Werttyp lebt inline, das heißt, überall dort, wo Ihre Variable oder Ihr Feld definiert ist.

Eine Variable, die a enthält Werttyp enthält das Ganze Werttyp Wert.Für eine Struktur bedeutet das, dass die Variable die gesamte Struktur mit allen ihren Feldern enthält.

Eine Variable, die a enthält Referenztyp enthält einen Zeiger oder einen Referenz an eine andere Stelle im Speicher, wo sich der tatsächliche Wert befindet.

Dies hat zunächst einmal einen Vorteil:

  • Werttypen enthält immer einen Wert
  • Referenztypen kann ein enthalten Null-Referenz, was bedeutet, dass sie sich im Moment überhaupt auf nichts beziehen

Im Inneren, Referenztyps werden als Zeiger implementiert, und wenn man weiß, dass und wie die Variablenzuweisung funktioniert, gibt es noch andere Verhaltensmuster:

  • Kopieren des Inhalts von a Werttyp Wenn Sie eine Variable in eine andere Variable kopieren, wird der gesamte Inhalt in die neue Variable kopiert, sodass die beiden unterschiedlich sind.Mit anderen Worten: Nach dem Kopieren wirken sich Änderungen an einem nicht auf das andere aus
  • Kopieren des Inhalts von a Referenztyp Variable in eine andere Variable kopieren, kopiert die Referenz, was bedeutet, dass Sie jetzt zwei Referenzen auf dieselbe haben irgendwo anders Speicherung der tatsächlichen Daten.Mit anderen Worten: Nach dem Kopieren scheint sich die Änderung der Daten in einer Referenz auch auf die andere zu auswirken, aber nur, weil Sie tatsächlich an beiden Stellen dieselben Daten betrachten

Wenn Sie Variablen oder Felder deklarieren, unterscheiden sich die beiden Typen wie folgt:

  • Variable: Werttyp lebt auf dem Stapel, Referenztyp bleibt auf dem Stapel als Zeiger auf irgendwo im Heap-Speicher, wo sich der eigentliche Speicher befindet (obwohl Hinweis Eric Lipperts Artikelserie:Der Stack ist ein Implementierungsdetail.)
  • Klasse/Strukturfeld: Werttyp lebt vollständig in der Art, Referenztyp lebt innerhalb des Typs als Zeiger auf eine Stelle im Heap-Speicher, wo sich der eigentliche Speicher befindet.

Andere Tipps

Eine kurze Zusammenfassung von jedem:

Nur Kurse:

  • Kann Vererbung unterstützen
  • Sind Referenztypen (Zeigertypen).
  • Die Referenz kann null sein
  • Es entsteht ein Speicher-Overhead pro neuer Instanz

Nur Strukturen:

  • Vererbung kann nicht unterstützt werden
  • Sind Werttypen
  • Werden als Wert übergeben (wie ganze Zahlen)
  • Kann keinen Nullverweis haben (es sei denn, Nullable wird verwendet)
  • Es entsteht kein Speicher-Overhead pro neuer Instanz – es sei denn, sie sind „boxed“.

Sowohl Klassen als auch Strukturen:

  • Werden in der Regel zusammengesetzte Datentypen verwendet, um einige Variablen zu enthalten, die in einer logischen Beziehung zueinander stehen
  • Kann Methoden und Ereignisse enthalten
  • Kann Schnittstellen unterstützen

In .NET unterscheiden die Struktur- und Klassendeklarationen zwischen Referenztypen und Werttypen.

Wenn Sie einen Referenztyp weitergeben, wird tatsächlich nur einer gespeichert.Der gesamte Code, der auf die Instanz zugreift, greift auf dieselbe Instanz zu.

Wenn Sie einen Werttyp weitergeben, ist jeder eine Kopie.Der gesamte Code arbeitet an einer eigenen Kopie.

Dies lässt sich an einem Beispiel zeigen:

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.

Bei einer 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 verweisen.

Strukturen sind der eigentliche Wert – sie können leer sein, aber niemals null.Aus diesem Grund haben Strukturen immer einen Standardkonstruktor ohne Parameter – sie benötigen einen „Startwert“.

Von Microsoft Zwischen Klasse und Struktur wählen ...

Als Faustregel sollte die meisten Typen in einem Rahmen Klassen sein.Es gibt jedoch einige Situationen, in denen die Eigenschaften eines Werttyps es angemessener für die Verwendung von Strukturen machen.

Betrachten Sie eine Struktur statt einer Klasse:

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

X Vermeiden Sie eine Struktur es sei denn, der Typ hat alle der folgenden Eigenschaften:

  • Es stellt logischerweise einen einzelnen Wert dar, ähnlich wie bei primitiven Typen (int, double usw.).
  • Die Instanzgröße beträgt weniger als 16 Byte.
  • Es ist unveränderlich. (kann nicht geändert werden)
  • Es muss nicht häufig verpackt werden.

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

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

Wenn Sie auf der Suche nach einem Video sind, das alle Unterschiede erklärt, können Sie es sich ansehen Teil 29 – C#-Tutorial – Unterschied zwischen Klassen und Strukturen in C#.

Instanzen von Klassen werden auf dem verwalteten Heap gespeichert.Alle Variablen, die eine Instanz „enthalten“, sind lediglich eine Referenz auf die Instanz auf dem Heap.Die Übergabe eines Objekts an eine Methode führt dazu, dass eine Kopie der Referenz übergeben wird, nicht das Objekt selbst.

Strukturen (technisch gesehen Werttypen) werden überall dort gespeichert, wo sie verwendet werden, ähnlich wie ein primitiver Typ.Der Inhalt kann von der Laufzeit jederzeit und ohne Aufruf eines benutzerdefinierten Kopierkonstruktors kopiert werden.Die Übergabe eines Werttyps an eine Methode erfordert das Kopieren des gesamten Werts, wiederum ohne den Aufruf von anpassbarem Code.

Die Unterscheidung wird durch die C++/CLI-Namen verbessert:„Ref-Klasse“ ist eine Klasse wie zuerst beschrieben, „Wertklasse“ ist eine Klasse wie zweitens beschrieben.Die Schlüsselwörter „class“ und „struct“, wie sie von C# verwendet werden, müssen einfach gelernt werden.

Unterschied zwischen Strukturen und Klassen:

  • Strukturen sind Werttypen wohingegen Klassen sind Referenztypen.
  • Strukturen werden auf dem Stapel gespeichert wohingegen Klassen werden auf dem Haufen gespeichert.
  • Werttypen halten ihren Wert im Speicher, in dem sie deklariert werden, aber der Referenztyp enthält einen Verweis auf einen Objektspeicher.
  • Werttypen werden sofort zerstört Nach dem Verlust des Umfangs, während der Referenztyp nur die Variable zerstört, nachdem der Umfang verloren gegangen ist.Das Objekt wird später vom Müllsammler zerstört.
  • Wenn Sie Struktur in eine andere Struktur kopieren, wird eine neue Kopie dieser Struktur erstellt, die von einer Struktur geändert wird, die den Wert der anderen Strukturen nicht beeinflusst.
  • Wenn Sie eine Klasse in eine andere Klasse kopieren, kopiert sie nur die Referenzvariable.
  • Beide Referenzvariablen verweisen auf dasselbe Objekt im Heap.Änderungen an einer Variablen wirken 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 Strukturen nicht unterstützt, unterstützt es die Vererbung, aber die Klassen.Beide unterstützen die Vererbung von einer Schnittstelle.
  • Strukturen sind vom versiegelten Typ.

Struktur vs. Klasse

Eine Struktur ist ein Werttyp und wird daher auf dem Stapel gespeichert. Eine Klasse ist jedoch ein Referenztyp und wird auf dem Heap gespeichert.

Eine Struktur unterstützt weder Vererbung noch Polymorphismus, aber eine Klasse unterstützt beides.

Standardmäßig sind alle Strukturmitglieder öffentlich, Klassenmitglieder sind jedoch standardmäßig privater Natur.

Da eine Struktur ein Werttyp ist, können wir einem Strukturobjekt keinen Nullwert zuweisen, bei einer Klasse ist dies jedoch nicht der Fall.

Der Vollständigkeit halber gibt es noch einen weiteren Unterschied bei der Verwendung von Equals Methode, 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 Main-Methode 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

Also, Strukturen eignen sich für numerisch ähnliche Objekte wie Punkte (x- und y-Koordinaten speichern).Und der Unterricht ist für andere geeignet.Selbst wenn zwei Personen denselben Namen, dieselbe Größe und dasselbe Gewicht haben, sind sie immer noch zwei Personen.

Zusätzlich zu den anderen Antworten gibt es einen grundlegenden Unterschied, der erwähnt werden sollte, und zwar die Art und Weise, wie er im Speicher gespeichert wird.Dies kann erhebliche Auswirkungen auf die Leistung von Arrays haben.Strukturen sind Werttypen, sie speichern also einen Wert in dem Speicherbereich, auf den sie zeigen. Klassen sind Referenztypen. Sie verweisen also auf eine Klasse in dem Speicherbereich, auf den sie zeigen. Der tatsächliche Wert wird an anderer Stelle gespeichert.

  • Mit einer Struktur wird innerhalb der enthaltenden Klasse Speicher zum Speichern der Daten zugewiesen.
  • Bei einer Klasse enthält die enthaltende Klasse lediglich einen Zeiger auf die neue Klasse in einem anderen Speicherbereich.

Dies gilt auch für Arrays, sodass ein Array von Strukturen im Speicher so aussieht

[struct][struct][struct][struct][struct][struct][struct][struct]

Wobei ein Array von Klassen so aussieht

[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]

Die tatsächlichen Werte, die Sie interessieren, werden 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 große Auswirkungen auf die Leistung des CPU-Cache.Die Verwendung von Klassen, wenn Sie Strukturen hätten verwenden können/sollten, wird die Anzahl der Cache-Fehler auf der CPU massiv erhöhen.

Das Langsamste, was eine moderne CPU tut, ist, keine Zahlen zu verarbeiten, sondern Daten aus dem Speicher abzurufen, und ein L1-Cache-Treffer ist um ein Vielfaches schneller als das Lesen von Daten aus dem RAM.

Zunächst einmal wird eine Struktur als Wert und nicht als Referenz übergeben.Strukturen eignen sich gut für relativ einfache Datenstrukturen, während Klassen aus architektonischer Sicht durch Polymorphismus und Vererbung viel mehr Flexibilität bieten.

Andere können Ihnen wahrscheinlich detailliertere Angaben machen als ich, aber ich verwende Strukturen, wenn die Struktur, die ich anstrebe, einfach ist.

Neben dem grundlegenden Unterschied des Zugriffsspezifizierers und einigen der oben genannten möchte ich einige der Hauptunterschiede, darunter einige der oben genannten, mit einem Codebeispiel mit Ausgabe hinzufügen, das eine klarere Vorstellung von der Referenz und dem Wert vermittelt

Strukturen:

  • Sind Werttypen und erfordern keine Heap-Zuweisung.
  • Die Speicherzuweisung ist unterschiedlich und wird im Stapel gespeichert
  • Nützlich für kleine Datenstrukturen
  • Beeinträchtigt die Leistung: Wenn wir einen 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 einer temporären Stelle auf dem Stapel), und dieser Wert wird dann nach Bedarf kopiert
  • Jede Variable verfügt über eine eigene Kopie der Daten, und es ist nicht möglich, dass sich Operationen an einer Variablen auf die andere auswirken.
  • Unterstützt keine benutzerspezifische Vererbung und erbt implizit vom Typobjekt

Klasse:

  • Referenztypwert
  • Im Heap gespeichert
  • Speichern Sie einen Verweis auf ein dynamisch zugewiesenes Objekt
  • Konstruktoren werden mit dem neuen Operator aufgerufen, aber dadurch wird kein Speicher auf dem Heap zugewiesen
  • Mehrere Variablen können einen Verweis auf dasselbe Objekt haben
  • Es ist möglich, dass Operationen an einer Variablen Auswirkungen auf das Objekt haben, 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 des Strukturobjekts ist:10

Innerhalb der Strukturmethode innerhalb des Methodenwerts des Strukturobjekts ist:20

Nach dem Methodenaufruf lautet der Wert des Strukturobjekts:10

Der Anfangswert des Klassenobjekts ist:10

In der Klassenmethode innerhalb des Methodenwerts des Klassenobjekts ist:20

Nach dem Methodenaufruf lautet der Wert des Klassenobjekts:20

Hier erkennt man deutlich den Unterschied zwischen Call-by-Value und Call-by-Reference.

  1. Bei in einer Klasse deklarierten Ereignissen wird der +=- und -=-Zugriff automatisch über eine Sperre (dieses) gesperrt, um sie threadsicher zu machen (statische Ereignisse sind für den Typ der Klasse gesperrt).Bei in einer Struktur deklarierten Ereignissen wird der +=- und -=-Zugriff nicht automatisch gesperrt.Eine Sperre (dieses) 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), wohingegen das Erstellen einer Referenztypinstanz zu einer Garbage Collection führen 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 dies bei einer Klasse möglicherweise nicht der Fall ist, da alle ihre Konstruktoren privat sein könnten.

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
    
  4. Eine Struktur kann keinen Destruktor haben.Ein Destruktor ist nur eine getarnte Überschreibung von object.Finalize, und Strukturen unterliegen als Werttypen nicht der Garbage Collection.

    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 schon.
    Eine Struktur kann nicht aufrufen:base() in ihrem Konstruktor, während eine Klasse ohne explizite Basisklasse dies kann.
    Eine Struktur kann eine andere Klasse nicht erweitern, eine Klasse schon.
    Eine Struktur kann keine geschützten Mitglieder (z. B. Felder, verschachtelte Typen) deklarieren, eine Klasse jedoch.
    Eine Struktur kann keine abstrakten Funktionsmitglieder deklarieren, eine abstrakte Klasse jedoch schon.
    Eine Struktur kann keine virtuellen Funktionsmitglieder deklarieren, eine Klasse jedoch schon.
    Eine Struktur kann keine versiegelten Funktionsmitglieder deklarieren, eine Klasse jedoch schon.
    Eine Struktur kann keine Override-Funktionsmitglieder deklarieren, eine Klasse jedoch schon.
    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.

Wie vorab erwähnt:Klassen sind Referenztypen, während Strukturen Werttypen mit allen Konsequenzen sind.

Als Faustregel empfehlen die Framework Design Guidelines die Verwendung von Structs anstelle von Klassen, wenn:

  • Die Instanzgröße beträgt weniger als 16 Byte
  • Es stellt logischerweise einen einzelnen Wert dar, ähnlich wie bei primitiven Typen (int, double usw.).
  • Es ist unveränderlich
  • Es muss nicht häufig verpackt werden
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                       |                                                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                                                                        |
| Access Modifiers      | public, private, internal                                                                            | public, protected, internal, protected internal, private protected                                |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+

Strukturen sind der eigentliche Wert – sie können leer sein, aber niemals null

Das stimmt, beachten Sie jedoch auch, dass Strukturen ab .NET 2 eine Nullable-Version unterstützen und C# etwas syntaktischen Zucker bereitstellt, um die Verwendung zu vereinfachen.

int? value = null;
value  = 1;

Es gibt einen interessanten Fall des Rätsels „Klasse vs. Struktur“ – eine Situation, in der Sie mehrere Ergebnisse der Methode zurückgeben müssen:Wählen Sie aus, was Sie verwenden möchten.Wenn Sie die ValueTuple-Geschichte kennen, wissen Sie, dass ValueTuple (Struktur) hinzugefügt wurde, weil es effektiver sein sollte als Tuple (Klasse).Aber was bedeutet das in Zahlen?Zwei Tests:Eine davon ist eine Struktur/Klasse mit 2 Feldern, die andere eine mit einer Struktur/Klasse mit 8 Feldern (mit einer Dimension von mehr als 4 – die Klasse sollte hinsichtlich der Prozessor-Ticks effektiver sein als die Struktur, aber natürlich sollte auch die GC-Last berücksichtigt werden).

P.S.Ein weiterer Benchmark für den spezifischen Fall „Struktur oder Klasse mit Sammlungen“ ist: 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;
            }
        }
    }
}

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 auf ein an anderer Stelle gespeichertes Objekt verweisen, auf das auch beliebig viele andere Referenzen existieren können.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 dort liegen können Teil von ein weiteres Heap-Objekt.

Wenn Sie eine Variable oder ein Feld eines primitiven Werttyps erstellen, wird diese mit einem Standardwert erstellt.Durch das Erstellen einer Variablen oder eines Felds eines Strukturtyps wird eine neue Instanz erstellt, in der alle darin enthaltenen Felder auf die Standardmethode erstellt werden.Ein Neues erstellen Beispiel eines Referenztyps erstellt zunächst alle darin enthaltenen Felder auf die Standardmethode und führt dann je nach Typ optionalen zusätzlichen Code aus.

Durch das Kopieren einer Variablen oder eines Felds eines primitiven Typs in ein anderes wird der Wert kopiert.Durch das Kopieren einer Variablen oder eines Felds eines Strukturtyps in eine andere werden alle Felder (öffentliche und private) der ersten Instanz in die zweite Instanz kopiert.Das Kopieren einer Variablen oder eines Felds eines Referenztyps in eine andere führt dazu, dass letztere auf dieselbe Instanz verweist wie erstere (falls vorhanden).

Es ist wichtig zu beachten, dass in einigen Sprachen wie C++ das semantische Verhalten eines Typs unabhängig davon ist, wie er gespeichert wird. Dies gilt jedoch nicht für .NET.Wenn ein Typ die Semantik veränderlicher Werte implementiert, kopiert das Kopieren einer Variablen dieses Typs in eine andere die Eigenschaften der ersten in eine andere Instanz, auf die die zweite verweist, und die Verwendung eines Mitglieds der zweiten zum Mutieren führt dazu, dass diese zweite Instanz geändert wird , 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 die erste Variable verweist.Typen mit unveränderlicher Semantik erlauben keine Mutation, daher spielt es semantisch keine Rolle, ob durch das Kopieren eine neue Instanz oder ein weiterer Verweis auf die erste erstellt wird.

In .NET ist es für Werttypen möglich, jede der oben genannten Semantiken zu implementieren, vorausgesetzt, alle ihre Felder können dies ebenfalls tun.Ein Referenztyp kann jedoch nur veränderliche Referenzsemantik oder unveränderliche Semantik implementieren;Werttypen mit Feldern veränderlicher Referenztypen sind entweder auf die Implementierung einer veränderbaren Referenzsemantik oder einer seltsamen Hybridsemantik beschränkt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top