Antworten:
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:
Intern werden Referenztypen als Zeiger implementiert. Wenn Sie wissen, dass und wie die Variablenzuweisung funktioniert, gibt es andere Verhaltensmuster:
Wenn Sie Variablen oder Felder deklarieren, unterscheiden sich die beiden Typen folgendermaßen:
Eine kurze Zusammenfassung von jedem:
Nur Klassen:
Nur Strukturen:
Sowohl Klassen als auch Strukturen:
c# struct memory overhead
und diese Antwort von Hans Passant gefunden, die besagt, dass nein, das ist auch nicht der Fall. Also, was meinst du?
class
verwaltetem Speicher sind (vom Garbage Collector verarbeitet), Instanzen von struct
nicht .
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'.
Unterschied zwischen Strukturen und Klassen:
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.
Zusätzlich zu allen in den anderen Antworten beschriebenen Unterschieden:
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 # .
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.
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| | 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 |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
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.
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.
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}");
}
Um es zu vervollständigen, gibt es einen weiteren Unterschied bei der Verwendung der 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 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.
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.
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:
Klasse:
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.
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.
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.
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
{
}
}
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
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.
Object
, der einen Verweis auf eine Box-Kopie der Struktur enthalten würde.
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 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;
}
}
}
}
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;
(object)(default(int?)) == null
dass 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>
.
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.