Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
16.1 Allgemein
Strukturen ähneln Klassen, da sie Datenstrukturen darstellen, die Datenmitglieder und Funktionsmitglieder enthalten können. Im Gegensatz zu Klassen sind Structs jedoch Werttypen und erfordern keine Heap-Zuweisung. Eine Variable eines struct
Typs enthält direkt die Daten des struct
Typs, während eine Variable eines Klassentyps einen Verweis auf die Daten enthält, der letztere als Objekt bezeichnet wird.
Hinweis: Strukturen sind besonders nützlich für kleine Datenstrukturen mit Wertsemantik. Komplexe Zahlen, Punkte in einem Koordinatensystem oder Schlüssel-Wert-Paare im Wörterbuch sind gute Beispiele für Strukturen. Der Schlüssel zu diesen Datenstrukturen besteht darin, dass sie nur wenige Datenmber haben, dass sie keine Vererbungs- oder Referenzsemantik benötigen, sondern sie können bequem mithilfe von Wertsemantik implementiert werden, bei denen die Zuordnung den Wert anstelle des Verweises kopiert. Hinweisende
Wie in §8.3.5 beschrieben, sind die von C# bereitgestellten einfachen Typen wie int
, double
und bool
tatsächlich alle Strukturtypen.
16.2 Struct-Deklarationen
16.2.1 Allgemein
Eine struct_declaration ist eine type_declaration (§14.7), die eine neue Struktur deklariert:
struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
Eine Konstruktdeklaration besteht aus einer optionalen Menge von Attributen (§22), gefolgt von einer optionalen Menge von Konstruktmodifikatorens (§16.2.2), gefolgt von einem optionalen ref
Modifikator (§16.2.3), gefolgt von einem optionalen partiellen Modifikator (§15.2.7), gefolgt von dem Schlüsselwort struct
und einem Identifikator , der die Struktur benennt, gefolgt von einer optionalen Typ-Parameter-Liste Spezifikation (§15.2.3), gefolgt von einer optionalen struct_interfaces Angabe (§16.2.5), gefolgt von einer optionalen type_parameter_constraints-clauses Angabe (§15.2.5), gefolgt von einer struct_body (§16.2.6), optional gefolgt von einem Semikolon.
Eine struct-Deklaration darf keine type_parameter_constraints_clauses liefern, wenn sie nicht auch eine type_parameter_listliefert.
Eine Strukturdeklaration, die eine type_parameter_list bereitstellt, ist eine generische Strukturdeklaration. Darüber hinaus ist jede Struktur, die in einer generischen Klassendeklaration oder einer generischen Strukturdeklaration geschachtelt ist, selbst eine generische Strukturdeklaration, da Typargumente für den enthaltenden Typ bereitgestellt werden müssen, um einen konstruierten Typ (§8.4) zu erstellen.
Eine Strukturdeklaration, die ein ref
Schlüsselwort enthält, darf keinen struct_interfaces Teil aufweisen.
16.2.2 Strukturmodifizierer
Optional kann eine struct_declaration eine Abfolge von struct_modifier enthalten.
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§23.2) ist nur im unsicheren Code (§23) verfügbar.
Es handelt sich um einen Kompilierungszeitfehler für denselben Modifizierer, der mehrmals in einer Strukturdeklaration angezeigt wird.
Mit Ausnahme von readonly
haben die Modifizierer einer Strukturdeklaration die gleiche Bedeutung wie die einer Klassendeklaration (§15.2.2).
Der readonly
Modifizierer gibt an, dass der struct_declaration einen Typ deklariert, dessen Instanzen unveränderlich sind.
Eine readonly-Struktur weist die folgenden Einschränkungen auf:
- Jedes seiner Instanzenfelder muss ebenfalls deklariert
readonly
werden. - Sie darf keine feldähnlichen Ereignisse (§15.8.2) deklarieren.
Wenn eine Instanz einer schreibgeschützten Struktur an eine Methode übergeben wird, wird ihr this
wie ein Eingabeargument/-parameter behandelt, wodurch Schreibzugriff auf alle Instanzfelder (außer durch Konstruktoren) nicht erlaubt ist.
16.2.3 Ref-Modifizierer
Der ref
Modifizierer gibt an, dass der struct_declaration einen Typ deklariert, dessen Instanzen im Ausführungsstapel zugeordnet sind. Diese Typen werden als Verweisstrukturtypen bezeichnet. Der ref
Modifizierer deklariert, dass Instanzen bezugsähnliche Felder enthalten können und nicht aus dem sicheren Kontext (§16.4.15) kopiert werden dürfen. Die Regeln zur Bestimmung des sicheren Kontexts einer Refstruktur werden in §16.4.15 beschrieben.
Es handelt sich um einen Kompilierungszeitfehler, wenn ein Verweisstrukturtyp in einem der folgenden Kontexte verwendet wird:
- Als Elementtyp eines Arrays.
- Als deklarierter Typ eines Felds einer Klasse oder einer Struktur, die nicht über den
ref
Modifizierer verfügt. - Auf
System.ValueType
oderSystem.Object
geboxt werden. - Als Typargument.
- Als Typ eines Tupel-Elements.
- Eine asynchrone Methode.
- Ein Iterator.
- Es gibt keine Konvertierung von einem
ref struct
Typ in den Typobject
oder den TypSystem.ValueType
. - Ein
ref struct
Typ darf nicht deklariert werden, um eine Schnittstelle zu implementieren. - Eine Instanzmethode, die in
object
oder inSystem.ValueType
deklariert, aber nicht in einemref struct
Typ überschrieben wird, darf nicht mit einem Empfänger diesesref struct
Typs aufgerufen werden. - Eine Instanzmethode eines
ref struct
-Typs darf nicht durch eine Methodengruppenkonvertierung in einen Delegatentyp erfasst werden. - Eine Referenzstruktur darf nicht durch einen Lambda-Ausdruck oder eine lokale Funktion erfasst werden.
Hinweis: A
ref struct
deklariert wederasync
Instanzmethoden noch eineyield return
yield break
Anweisung innerhalb einer Instanzmethode, da der implizitethis
Parameter in diesen Kontexten nicht verwendet werden kann. Hinweisende
Diese Einschränkungen stellen sicher, dass eine Variable vom ref struct
Typ nicht auf nicht mehr gültigen Stapelspeicher oder auf Variablen verweist, die nicht mehr gültig sind.
16.2.4 Teilmodifizierer
Der partial
Modifizierer gibt an, dass diese struct_declaration eine partielle Typdeklaration ist. Mehrere partielle Strukturdeklarationen mit demselben Namen innerhalb eines umschließenden Namespaces oder einer Typdeklaration werden gemäß den in §15.2.7 angegebenen Regeln zu einer einzigen Strukturdeklaration zusammengefasst.
16.2.5 Strukturschnittstellen
Eine Strukturdeklaration kann eine struct_interfaces Spezifikation enthalten, in diesem Fall wird die Struktur angewiesen, die angegebenen Schnittstellentypen direkt zu implementieren. Für einen konstruierten Strukturtyp, einschließlich eines geschachtelten Typs, der in einer generischen Typdeklaration (§15.3.9.7) deklariert ist, wird jeder implementierte Schnittstellentyp durch Ersetzen jedes type_parameter in der angegebenen Schnittstelle durch die entsprechende type_argument des konstruierten Typs abgerufen.
struct_interfaces
: ':' interface_type_list
;
Die Behandlung von Schnittstellen zu mehreren Teilen einer Teilstrukturerklärung (§15.2.7) wird in §15.2.4.3 weiter erörtert.
Schnittstellenimplementierungen werden in §18.6 weiter erörtert.
16.2.6 Strukturkörper
Die struct_body einer Struktur definiert die Member der Struktur.
struct_body
: '{' struct_member_declaration* '}'
;
16.3 Strukturmitglieder
Die Mitglieder einer Struktur bestehen aus den Mitgliedern, die durch ihre struct_member_declarations eingeführt werden, und den vom Typ System.ValueType
geerbten Mitgliedern.
struct_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| static_constructor_declaration
| type_declaration
| fixed_size_buffer_declaration // unsafe code support
;
fixed_size_buffer_declaration (§23.8.2) ist nur im unsicheren Code (§23) verfügbar.
Hinweis: Alle Arten von class_member_declarationaußer finalizer_declaration sind auch struct_member_declarations. Hinweisende
Mit Ausnahme der in §16.4 genannten Unterschiede gelten auch die Beschreibungen von Klassenmitgliedern gemäß §15.3 bis §15.12 auch für Strukturmitglieder.
16.4 Klassen- und Strukturunterschiede
16.4.1 Allgemein
Die Struktur unterscheidet sich von Klassen auf verschiedene wichtige Arten:
- Strukturen sind Werttypen (§16.4.2).
- Alle Strukturtypen erben implizit von der Klasse
System.ValueType
(§16.4.3). - Die Zuordnung zu einer Variablen eines Strukturtyps erstellt eine Kopie des zugewiesenen Werts (§16.4.4).
- Der Standardwert einer Struktur ist der Wert, der durch Festlegen aller Felder auf den Standardwert (§16.4.5) erzeugt wird.
- Boxing- und Unboxing-Operationen werden verwendet, um zwischen einem struct-Typ und bestimmten Referenztypen zu konvertieren (§16.4.6).
- Die Bedeutung von
this
ist innerhalb der Strukturmitglieder (§16.4.7) unterschiedlich. - Instanzenfelddeklarationen für eine Struktur dürfen keine variablen Initialisierer (§16.4.8) enthalten.
- Eine Struktur darf keinen parameterlosen Instanzkonstruktor (§16.4.9) deklarieren.
- Eine Struktur darf keinen Finalizer deklarieren.
- Ereignisdeklarationen, Eigenschaftsdeklarationen, Eigenschaftenaccessoren, Indexerdeklarationen und Methodendeklarationen dürfen den Modifizierer
readonly
aufweisen, während dies für dieselben Membertypen in Klassen nicht allgemein zulässig ist.
16.4.2 Wertsemantik
Structs sind Werttypen (§8.3) und besitzen sogenannte Wertsemantik. Klassen hingegen sind Referenztypen (§8.2) und sollen Referenzsemantik aufweisen.
Eine Variable eines Strukturtyps enthält direkt die Daten der Struktur, während eine Variable eines Klassentyps einen Verweis auf ein Objekt enthält, das die Daten enthält. Wenn eine Struktur B
ein Instanzfeld vom Typ A
enthält und A
ein Strukturtyp ist, führt es zu einem Kompilierungszeitfehler, wenn A
von B
abhängig ist oder aus einem Typ B
erstellt wird. Eine Struktur X
hängt direkt von einer Struktur Y
ab, wenn X
ein Instanzfeld vom Typ Y
enthält. Angesichts dieser Definition ist die vollständige Menge der Strukturen, von denen eine Struktur abhängt, der transitive Abschluss der Beziehung direkt abhängig von .
Beispiel:
struct Node { int data; Node next; // error, Node directly depends on itself }
ist ein Fehler, weil
Node
ein Instanzfeld seines eigenen Typs enthält. Ein weiteres Beispielstruct A { B b; } struct B { C c; } struct C { A a; }
ist ein Fehler, da jeder der Typen
A
,B
undC
von einander abhängig ist.Endbeispiel
Bei Klassen ist es möglich, dass zwei Variablen auf dasselbe Objekt verweisen, und somit kann eine Operation auf eine Variable das Objekt beeinflussen, auf das von der anderen Variable verwiesen wird. Bei Structs hat jede Variable ihre eigene Kopie der Daten (mit Ausnahme von By-Reference-Parametern), und es ist nicht möglich, dass Operationen auf eine Variable die andere beeinflussen. Außer wenn explizit NULL-Werte (§8.3.12) zulässig sind, ist es nicht möglich, dass Werte eines Strukturtyps null
sind.
Hinweis: Wenn eine Struktur ein Feld vom Referenztyp enthält, kann der Inhalt des Objekts, auf das verwiesen wird, von anderen Vorgängen geändert werden. Der Wert des Felds selbst, d. h. das Objekt, auf das es verweist, kann jedoch nicht durch eine Mutation eines anderen Strukturwerts geändert werden. Hinweisende
Beispiel: Gegeben sei das Folgende
struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point a = new Point(10, 10); Point b = a; a.x = 100; Console.WriteLine(b.x); } }
die Ausgabe ist
10
. Die Zuordnung vona
zub
erstellt eine Kopie des Werts, undb
ist somit von der Zuordnung zua.x
unberührt. WärePoint
stattdessen als Klasse deklariert worden, wäre die Ausgabe100
, weila
undb
auf dasselbe Objekt verweisen würden.Endbeispiel
16.4.3 Vererbung
Alle Strukturtypen erben implizit von der Klasse System.ValueType
, die wiederum von der Klasse object
erbt. Eine Strukturdeklaration kann eine Liste der implementierten Schnittstellen angeben, aber es ist nicht möglich, dass eine Strukturdeklaration eine Basisklasse angeben kann.
Strukturtypen sind nie abstrakt und werden immer implizit versiegelt. Die Modifizierer abstract
und sealed
sind daher in einer Strukturdeklaration nicht zulässig.
Da die Vererbung für Structs nicht unterstützt wird, kann die deklarierte Sichtbarkeit eines Strukturelements nicht protected
, private protected
oder protected internal
sein.
Funktionsmember in einem struct können nicht abstrakt oder virtuell sein, und der override
Modifizierer darf nur Methoden außer Kraft setzen, die von System.ValueType
geerbt wurden.
16.4.4 Zuordnung
Die Zuordnung zu einer Variablen eines Strukturtyps erstellt eine Kopie des zugewiesenen Werts. Dies unterscheidet sich von der Zuweisung zu einer Variablen eines Klassentyps, die den Verweis kopiert, aber nicht das durch den Verweis identifizierte Objekt.
Ähnlich wie bei einer Zuordnung wird eine Kopie der Struktur erstellt, wenn eine Struktur als Wertparameter übergeben oder als Ergebnis eines Funktionselements zurückgegeben wird. Eine Struktur kann mithilfe eines By-Reference-Parameters durch Verweis auf ein Funktionselement übergeben werden.
Wenn eine Eigenschaft oder ein Indexer einer Struktur das Ziel einer Zuordnung ist, muss der Instanzausdruck, der der Eigenschaft oder dem Indexerzugriff zugeordnet ist, als Variable klassifiziert werden. Wenn der Instanzausdruck als Wert klassifiziert wird, tritt ein Kompilierzeitfehler auf. Dies wird in §12.21.2 ausführlich beschrieben.
16.4.5 Standardwerte
Wie in §9.3 beschrieben, werden verschiedene Arten von Variablen automatisch auf ihren Standardwert initialisiert, wenn sie erstellt werden. Für Variablen von Klassentypen und anderen Verweistypen ist null
dieser Standardwert . Da Structs jedoch Werttypen sind, die nicht sein null
können, ist der Standardwert einer Struktur der Wert, der erzeugt wird, indem alle Werttypfelder auf ihren Standardwert und alle Bezugstypfelder festgelegt werden.null
Beispiel: Verweisen auf die
Point
oben deklarierte Struktur, das BeispielPoint[] a = new Point[100];
initialisiert jedes Element im Array
Point
auf den Wert, der durch Setzen der Felderx
undy
auf Null erzeugt wird.Endbeispiel
Der Standardwert einer Struktur entspricht dem Wert, der vom Standardkonstruktor der Struktur zurückgegeben wird (§8.3.3). Im Gegensatz zu einer Klasse darf eine Struktur keinen parameterlosen Instanzkonstruktor deklarieren. Stattdessen verfügt jede Struktur implizit über einen parameterlosen Instanzkonstruktor, der immer den Wert zurückgibt, der daraus resultiert, dass alle Felder auf ihre Standardwerte festgelegt werden.
Hinweis: Strukturen sollten so konzipiert sein, dass sie den Standardinitialisierungszustand als gültigen Zustand betrachten. Im Beispiel
struct KeyValuePair { string key; string value; public KeyValuePair(string key, string value) { if (key == null || value == null) { throw new ArgumentException(); } this.key = key; this.value = value; } }
Der benutzerdefinierte Instanzkonstruktor schützt nur dann vor
null
Werten, wenn er explizit aufgerufen wird. In Fällen, in denen eineKeyValuePair
Variable der Standardwertinitialisierung unterliegt, sind diekey
value
Feldernull
, und die Struktur sollte darauf vorbereitet sein, diesen Zustand zu verarbeiten.Hinweisende
16.4.6 Boxing und Unboxing
Ein Wert eines Klassentyps kann in Typ object
oder in einen Schnittstellentyp konvertiert werden, der von der Klasse implementiert wird, indem der Verweis während der Kompilierung in einen anderen Typ umgewandelt wird. Ebenso kann ein Wert vom Typ object
oder ein Wert eines Schnittstellentyps wieder in einen Klassentyp konvertiert werden, ohne den Verweis zu ändern (in diesem Fall ist jedoch eine Laufzeittypüberprüfung erforderlich).
Da Strukturtypen keine Referenztypen sind, werden diese Vorgänge für Strukturtypen unterschiedlich implementiert. Wenn ein Wert eines Strukturtyps in bestimmte Bezugstypen (gemäß §10.2.9) konvertiert wird, erfolgt ein Boxvorgang. Ebenso findet eine Unboxing-Operation statt, wenn ein Wert bestimmter Referenztypen (wie in §10.3.7definiert) zurück in einen struct-Typ konvertiert wird. Ein wesentlicher Unterschied zu den gleichen Operationen bei Klassentypen besteht darin, dass beim boxing und unboxing der Wert der Struktur entweder in die geboxte Instanz hinein oder aus ihr heraus kopiert wird.
Hinweis: So werden nach einem Boxing- oder Unboxing-Vorgang Änderungen, die an dem unboxed
struct
vorgenommen wurden, nicht in dem boxed wiedergegebenstruct
. Hinweisende
Weitere Informationen zum Boxen und Entpacken finden Sie unter §10.2.9 und §10.3.7.
16.4.7 Bedeutung dieser
Die Bedeutung einer this
Struktur unterscheidet sich von der Bedeutung einer this
Klasse, wie in §12.8.14 beschrieben. Wenn ein struct-Typ eine von System.ValueType
geerbte virtuelle Methode außer Kraft setzt (z.B. Equals
, GetHashCode
oder ToString
), führt der Aufruf der virtuellen Methode durch eine Instanz des struct-Typs nicht zu Boxing. Dies gilt auch dann, wenn die Struktur als Typparameter verwendet wird und der Aufruf über eine Instanz des Typparametertyps erfolgt.
Beispiel:
struct Counter { int value; public override string ToString() { value++; return value.ToString(); } } class Program { static void Test<T>() where T : new() { T x = new T(); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); } static void Main() => Test<Counter>(); }
Die Ausgabe des Programms ist:
1 2 3
Obwohl es schlechter Stil ist, wenn
ToString
Nebenwirkungen hat, zeigt das Beispiel, dass bei den drei Aufrufen vonx.ToString()
kein Boxen auftritt.Endbeispiel
Ebenso tritt Boxing nie implizit auf, wenn auf ein Mitglied mit einem eingeschränkten Typparameter zugegriffen wird, wenn das Mitglied innerhalb des Werttyps implementiert ist. Angenommen, eine Schnittstelle ICounter
enthält eine Methode Increment
, die zum Ändern eines Werts verwendet werden kann. Wenn ICounter
als Einschränkung verwendet wird, wird die Implementierung der Methode Increment
mit einem Verweis auf die Variable aufgerufen, für die Increment
aufgerufen wurde, niemals mit einer verpackten Kopie.
Beispiel:
interface ICounter { void Increment(); } struct Counter : ICounter { int value; public override string ToString() => value.ToString(); void ICounter.Increment() => value++; } class Program { static void Test<T>() where T : ICounter, new() { T x = new T(); Console.WriteLine(x); x.Increment(); // Modify x Console.WriteLine(x); ((ICounter)x).Increment(); // Modify boxed copy of x Console.WriteLine(x); } static void Main() => Test<Counter>(); }
Der erste Aufruf von
Increment
ändert den Wert in der Variablenx
. Dies ist nicht gleichbedeutend mit dem zweiten Aufruf vonIncrement
, der den Wert in einer geboxten Kopie vonx
ändert. Die Ausgabe des Programms ist also:0 1 1
Endbeispiel
16.4.8 Feldinitialisierer
Wie in §16.4.5 beschrieben, besteht der Standardwert einer Struktur aus dem Wert, der sich ergibt, wenn alle Werttypfelder auf ihren Standardwert gesetzt werden und alle Bezugstypfelder auf null
. Aus diesem Grund erlaubt eine Struktur keine Instanzfelddeklarationen, die Variableninitialisierer enthalten. Diese Einschränkung gilt nur für Instanzfelder. Statische Felder einer Struktur dürfen variable Initialisierer enthalten.
Beispiel: Das Folgende
struct Point { public int x = 1; // Error, initializer not permitted public int y = 1; // Error, initializer not permitted }
ist fehlerhaft, da die Instanzfelddeklarationen variable Initialisierer enthalten.
Endbeispiel
Eine field_declaration , die direkt innerhalb einer struct_declaration mit dem struct_modifierreadonly
deklariert wird, hat die field_modifierreadonly
.
16.4.9 Konstruktoren
Im Gegensatz zu einer Klasse darf eine Struktur keinen parameterlosen Instanzkonstruktor deklarieren. Stattdessen verfügt jede Struktur implizit über einen parameterlosen Instanzkonstruktor, der immer den Wert zurückgibt, der sich aus dem Festlegen aller Werttypfelder auf den Standardwert und alle Bezugstypfelder auf null
(§8.3.3) ergibt. Eine Struktur kann Instanzkonstruktoren mit Parametern deklarieren.
Beispiel: Gegeben sei das Folgende
struct Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point p1 = new Point(); Point p2 = new Point(0, 0); } }
die Anweisungen erstellen beide ein
Point
mitx
undy
initialisiert auf Null.Endbeispiel
Ein Strukturinstanzkonstruktor darf keinen Konstruktorinitialisierer des Formulars base(
argument_list)
enthalten, wobei argument_list optional ist.
Der this
Parameter eines Strukturinstanzkonstruktors entspricht einem Ausgabeparameter des Strukturtyps. Als solches muss this
an jeder Stelle, an der der Konstruktor zurückkehrt, definitiv zugewiesen werden (§9.4). Ebenso kann es nicht (auch nicht implizit) im Konstruktorkörper gelesen werden, bevor es definitiv zugewiesen wird.
Wenn der Strukturinstanzkonstruktor einen Konstruktorinitialisierer angibt, wird dieser Initialisierer als eine definitive Zuweisung dazu betrachtet, die vor dem Text des Konstruktors auftritt. Daher hat der Körper selbst keine Initialisierungsanforderungen.
Beispiel: Betrachten Sie die Implementierung des Instanzkonstruktors unten:
struct Point { int x, y; public int X { set { x = value; } } public int Y { set { y = value; } } public Point(int x, int y) { X = x; // error, this is not yet definitely assigned Y = y; // error, this is not yet definitely assigned } }
Kein Instanzfunktionsmitglied (einschließlich der Set-Accessoren für die Eigenschaften
X
undY
) kann aufgerufen werden, bis alle Felder der zu erstellenden Struktur definitiv zugewiesen wurden. Beachten Sie jedoch, dass beiPoint
einer Klasse anstelle einer Struktur die Implementierung des Instanzkonstruktors zulässig wäre. Es gibt eine Ausnahme davon und umfasst automatisch implementierte Eigenschaften (§15.7.4). Die Regeln für die definitive Zuweisung (§12.21.2) nehmen die Zuweisung zu einer Auto-Eigenschaft eines struct-Typs innerhalb eines Instanzkonstruktors dieses struct-Typs ausdrücklich aus: eine solche Zuweisung wird als definitive Zuweisung des versteckten Backing-Feldes der Auto-Eigenschaft betrachtet. Daher ist Folgendes zulässig:struct Point { public int X { get; set; } public int Y { get; set; } public Point(int x, int y) { X = x; // allowed, definitely assigns backing field Y = y; // allowed, definitely assigns backing field } }
Endbeispiel]
16.4.10 Statische Konstruktoren
Statische Konstruktoren für Strukturen folgen den meisten der gleichen Regeln wie für Klassen. Die Ausführung eines statischen Konstruktors für einen Strukturtyp wird durch die ersten der folgenden Ereignisse ausgelöst, die in einer Anwendungsdomäne auftreten:
- Auf ein statisches Element des Strukturtyps wird verwiesen.
- Ein explizit deklarierter Konstruktor des Strukturtyps wird aufgerufen.
Hinweis: Die Erstellung von Standardwerten (§16.4.5) von Strukturtypen löst den statischen Konstruktor nicht aus. (Ein Beispiel hierfür ist der Anfangswert von Elementen in einem Array.) Endnote
16.4.11 Eigenschaften
Ein property_declaration (§15.7.1) für eine Instanz-Eigenschaft in einer Strukturdeklaration kann den Eigenschaftsmodifikatorreadonly
enthalten. Eine statische Eigenschaft darf diesen Modifizierer jedoch nicht enthalten.
Es ist ein Kompilierungsfehler, den Zustand einer Instanzvariable der Struktur über eine in dieser Struktur deklarierte readonly-Eigenschaft zu ändern.
Es handelt sich um einen Kompilierungszeitfehler, wenn eine automatisch implementierte Eigenschaft mit einem readonly
Modifikator auch einen set
Accessor hat.
Es handelt sich um einen Kompilierungszeitfehler für eine automatisch implementierte Eigenschaft in einer readonly
Struktur, die über einen set
Accessor verfügt.
Eine automatisch implementierte Eigenschaft, die in einer readonly
Struktur deklariert ist, muss keinen readonly
Modifizierer aufweisen, da der get
Accessor implizit als schreibgeschützt betrachtet wird.
Es handelt sich um einen Kompilierungsfehler, einen readonly
-Modifizierer sowohl auf einer Eigenschaft selbst als auch auf einem ihrer get
- oder set
-Accessoren zu haben.
Es ist ein Kompilierungsfehler, wenn eine Eigenschaft bei all ihren Accessoren einen nur-lese Modifizierer hat.
Hinweis: Um den Fehler zu beheben, verschieben Sie den Modifizierer von den Accessoren in die Eigenschaft selbst. Hinweisende
Automatisch implementierte Eigenschaften (§15.7.4) verwenden versteckte Hintergrundfelder, auf die nur die Eigenschaften-Accessoren zugreifen können.
Hinweis: Diese Zugriffsbeschränkung bedeutet, dass Konstruktoren, die automatisch implementierte Eigenschaften enthalten, häufig einen expliziten Konstruktorinitialisierer benötigen, wo sie andernfalls keinen benötigen, um die Anforderung aller Felder zu erfüllen, die definitiv zugewiesen werden, bevor ein Funktionsmememm aufgerufen wird oder der Konstruktor zurückgegeben wird. Hinweisende
16.4.12 Methoden
Ein method_declaration (§15.6.1) für eine Instanzmethode in einem struct_declaration kann die method_modifierreadonly
enthalten. Eine statische Methode darf diesen Modifizierer jedoch nicht enthalten.
Es ist ein Kompilierungsfehler, zu versuchen, den Zustand einer Strukturinstanz-Variablen über eine in dieser Struktur deklarierte Readonly-Methode zu ändern.
Obwohl eine readonly-Methode eine gleichgeordnete, nicht readonly-Methode oder eine Eigenschaft oder einen Indexer-Zugriffsaccessor aufrufen kann, führt dies zur Erstellung einer impliziten Kopie von this
als Schutzmaßnahme.
Eine readonly-Methode kann eine gleichgeordnete Eigenschaft oder einen Indexersatz-Accessor aufrufen, der schreibgeschützt ist. Wenn der Accessor eines Geschwistermitglieds nicht explizit oder implizit schreibgeschützt ist, tritt ein Kompilierungsfehler auf.
Alle Methodendeklarationen der Teilmethoden müssen einen readonly
Modifizierer haben, oder keiner von ihnen darf ihn haben.
16.4.13 Indexer
Ein Indexer-Deklaration (§15.9) für einen Instanzindexer in einer Strukturdeklaration kann den indexer_modifierreadonly
enthalten.
Es handelt sich um einen Kompilierungsfehler, den Zustand einer Instanz einer Strukturvariablen über einen in dieser Struktur deklarierten schreibgeschützten Indexer zu ändern.
Es handelt sich um einen Kompilierungszeitfehler, um einen Modifizierer für einen readonly
Indexer selbst sowie für seine get
Accessoren oder set
Accessoren zu verwenden.
Es handelt sich um einen Kompilierungszeitfehler für einen Indexer, der einen readonly modifier für alle Accessoren hat.
Hinweis: Um den Fehler zu beheben, verschieben Sie den Modifizierer von den Accessoren in den Indexer selbst. Hinweisende
16.4.14 Ereignisse
Ein event_declaration (§15.8.1) für ein beispielweises nicht feldähnliches Ereignis in einem struct_declaration kann den event_modifierreadonly
enthalten. Ein statisches Ereignis darf diesen Modifizierer jedoch nicht enthalten.
Einschränkung des sicheren Kontexts
16.4.15.1 Allgemein
Zur Kompilierungszeit wird jeder Ausdruck einem Kontext zugeordnet, in dem auf diese Instanz und alle zugehörigen Felder sicher zugegriffen werden kann, der sichere Kontext. Der sichere Kontext ist ein Kontext, der einen Ausdruck umschließt, in den der Wert sicher gelangen kann.
Jeder Ausdruck, dessen Kompilierungszeittyp keine Ref-Struktur ist, hat einen sicheren Kontext des Aufruferkontextes.
Ein default
-Ausdruck, egal welchen Typs, hat den sicheren Kontext des Aufrufer-Kontextes.
Für jeden Nicht-Standardausdruck, dessen Kompilierzeittyp eine Ref-Struktur ist, wird ein sicherer Kontext durch die folgenden Abschnitte definiert.
Der sichere Kontext zeichnet auf, in welchen Kontext ein Wert kopiert werden darf. Wenn eine Zuweisung von einem Ausdruck E1
mit einem sicheren Kontext S1
zu einem Ausdruck E2
mit sicherem Kontext S2
erfolgt, handelt es sich um einen Fehler, wenn S2
ein breiterer Kontext ist als S1
.
Es gibt drei verschiedene Werte für sichere Kontexte, die identisch sind mit den für Referenzvariablen definierten Werten (§9.7.2): Deklarationsblock, Funktionsmitglied und Aufruferkontext. Der sichere Kontext eines Ausdrucks schränkt die Verwendung wie folgt ein:
- Bei einer Rückgabeanweisung
return e1
muss der sichere Kontext vone1
der Aufrufer-Kontext sein. - Für eine Zuordnung
e1 = e2
muss der sichere Kontexte2
mindestens so breit wie der sichere Kontext seine1
.
Bei einem Methodenaufruf, wenn ein ref
oder out
Argument eines ref struct
Typs vorhanden ist (einschließlich des Empfängers, es sei denn, der Typ ist readonly
) und der sichere Kontext S1
ist, dann darf kein Argument (einschließlich des Empfängers) einen schmaleren sicheren Kontext aufweisen als S1
.
16.4.15.2 Parameter sicherer Kontext
Ein Parameter eines ref struct-Typs, einschließlich des this
-Parameters einer Instanzmethode, hat einen safe-context von caller-context.
16.4.15.3 Lokaler variabler sicherer Kontext
Eine lokale Variable eines Verweisstrukturtyps weist wie folgt einen sicheren Kontext auf:
- Wenn es sich bei der Variablen um eine Iterationsvariable einer
foreach
Schleife handelt, entspricht der sichere Kontext der Variablen dem sicheren Kontext des Ausdrucks derforeach
Schleife. - Andernfalls, wenn die Deklaration der Variablen einen Initialisierer hat, entspricht der sichere Kontext der Variablen dem sicheren Kontext dieses Initialisierers.
- Andernfalls wird die Variable am Deklarationspunkt nicht initialisiert und verfügt über einen sicheren Kontext des Aufruferskontexts.
16.4.15.4 Feldsicherer Kontext
Ein Verweis auf ein Feld e.F
, bei dem der Typ von F
ein Refstrukturtyp ist, hat einen sicheren Kontext, der derselbe wie der sichere Kontext von e
ist.
16.4.15.5 Betreiber
Die Anwendung eines benutzerdefinierten Operators wird als Methodenaufruf behandelt (§16.4.15.6).
Bei einem Operator, der einen Wert liefert, wie e1 + e2
oder c ? e1 : e2
, ist der sichere Kontext des Ergebnisses der engste Kontext unter den sicheren Kontexten der Operanden des Operators. Folglich ist für einen unären Operator, der einen Wert liefert, wie +e
, der sichere Kontext des Ergebnisses gleich dem sicheren Kontext des Operanden.
Hinweis: Der erste Operand eines bedingten Operators ist ein
bool
, sodass der sichere Kontext der Aufruferkontext ist. Daraus folgt, dass der resultierende sichere Kontext der engste sichere Kontext des zweiten und dritten Operanden ist. Hinweisende
16.4.15.6 Methode und Eigenschaftsaufruf
Ein Wert, der sich aus einem Methodenaufruf e1.M(e2, ...)
oder einem Eigenschaftsaufruf e.P
ergibt, hat einen sicheren Kontext des kleinsten der folgenden Kontexte:
- Anrufer-Kontext
- Der sichere Kontext aller Argumentausdrücke (einschließlich des Empfängers).
Ein Eigenschaftsaufruf (entweder get
oder set
) wird als Methodenaufruf der zugrunde liegenden Methode durch die obigen Regeln behandelt.
16.4.15.7 stackalloc
Das Ergebnis eines stackalloc-Ausdrucks hat den sicheren Kontext von function-member.
16.4.15.8 Konstruktoraufrufe
Ein new
Ausdruck, der einen Konstruktor aufruft, entspricht den gleichen Regeln wie ein Methodenaufruf, der als Rückgabe des erstellten Typs gilt.
Darüber hinaus ist der sichere Kontext der kleinste der sicheren Kontexte aller Argumente und Operanden aller Objektinitialisierungsausdrücke, rekursiv, wenn ein Initialisierer vorhanden ist.
Hinweis: Diese Regeln basieren darauf
Span<T>
, dass kein Konstruktor der folgenden Form vorhanden ist:public Span<T>(ref T p)
Ein solcher Konstruktor macht die Instanzen von
Span<T>
, die als Felder verwendet werden, von einemref
Feld nicht unterscheidbar. Die in diesem Dokument beschriebenen Sicherheitsregeln beruhen darauf, dassref
-Felder in C# oder .NET kein gültiges Konstrukt sind. Hinweisende
ECMA C# draft specification