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 structTyps, 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.8), die eine neue Struktur deklariert:
struct_declaration
: non_record_struct_declaration
| record_struct_declaration
;
non_record_struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
record_struct_declaration
: attributes? struct_modifier* 'partial'? 'record' 'struct'
identifier type_parameter_list? delimited_parameter_list? struct_interfaces?
type_parameter_constraints_clause* record_struct_body
;
record_struct_body
: struct_body ';'?
| ';'
;
Ein struct_declaration ist entweder für eine Nicht-Datensatz-Struktur oder eine Datensatzstruktur vorgesehen.
Ein non_record_struct_declaration besteht aus einem optionalen Satz von Attributen (§23), gefolgt von einem optionalen Satz von struct_modifiers (§16.2.2), gefolgt von einem optionalen ref Modifizierer (§16.2.3), gefolgt von einem optionalen Teilmodifizierer (§15.2.7), gefolgt von dem Schlüsselwort struct und einem Bezeichner , der die Struktur benennt, gefolgt von einer optionalen type_parameter_list Spezifikation (§15.2.3), gefolgt von einer optionalen struct_interfaces Spezifikation (§16.2.5), gefolgt von einer optionalen type_parameter_constraints-Klauselnspezifikation (§15.2.5), gefolgt von einem struct_body (§16.2.6), optional gefolgt von einem Semikolon.
Ein record_struct_declaration besteht aus einem optionalen Satz von Attributen (§23), gefolgt von einem optionalen Satz von struct_modifiers (§16.2.2), gefolgt von einem optionalen Teilmodifizierer (§15.2.7), gefolgt von dem Schlüsselwort recordstruct und einem Bezeichner, der die Struktur benennt, gefolgt von einer optionalen type_parameter_list Spezifikation (§15.2.3), gefolgt von einer optionalen delimited_parameter_list Spezifikation (§15.2.1), gefolgt von einer optionalen struct_interfaces Spezifikation (§16.2.5), gefolgt von einer optionalen type_parameter_constraints-Klauselnspezifikation (§15.2.5), gefolgt von einem record_struct_body.
Eine struct_declaration darf type_parameter_constraints_clause nicht liefern ,es sei denn, sie liefert auch eine type_parameter_list.
Eine struct_declaration , 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.
Ein non_record_struct_declaration , der einen ref Modifizierer enthält, darf keinen struct_interfaces Teil aufweisen.
Ein record_struct_declaration eine delimited_parameter_list deklariert eine positionale Datensatzstruktur.
Höchstens ein record_struct_declaration, der eine delimited_parameter_list enthältpartial.
Die Parameter in delimited_parameter_list dürfen jedoch paramsin weder refoutthis über Modifizierer noch über Modifizierer verfügen.
Bei einem record_struct_declaration entsprechen die record_struct_bodys {}{};, und ; sind gleichwertig. Sie alle weisen darauf hin, dass die einzigen Member vom Compiler synthetisiert werden (§16.4).
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 (§24.2) ist nur im unsicheren Code (§24) 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
readonlywerden. - 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 non_record_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 dürfen nicht aus dem sicheren Kontext (§16.5.15) kopiert werden. Die Regeln zur Bestimmung des sicheren Kontexts einer Refstruktur werden in §16.5.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
refModifizierer verfügt. - Als Typargument.
- Als Typ eines Tupel-Elements.
- In einer asynchronen Methode.
- In einem Iterator.
- Als Empfängertyp für eine Methodengruppenkonvertierung von einer Instanzmethode in einen Delegattyp.
- Als erfasste Variable in einem Lambda-Ausdruck oder einer lokalen Funktion.
Darüber hinaus gelten die folgenden Einschränkungen für einen ref struct Typ:
- Ein
ref structTyp darf nicht in oderSystem.ValueType.System.Object - Ein
ref structTyp darf nicht deklariert werden, um eine Schnittstelle zu implementieren. - Eine Instanzmethode, die in
objectoder inSystem.ValueTypedeklariert, aber nicht in einemref structTyp überschrieben wird, darf nicht mit einem Empfänger diesesref structTyps aufgerufen werden.
Hinweis: A
ref structdeklariert wederasyncInstanzmethoden noch eineyield returnyield breakAnweisung innerhalb einer Instanzmethode, da der implizitethisParameter 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 §19.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
16.3.1 Allgemein
Die Mitglieder einer Struktur bestehen aus den Mitgliedern, die durch ihre struct_member_declarations eingeführt werden, und den vom Typ System.ValueTypegeerbten Mitgliedern. Für eine Datensatzstruktur enthält der Membersatz auch die vom Compiler generierten synthetisierten Member (§synth-members).
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 (§24.8.2) ist nur im unsicheren Code (§24) verfügbar.
Hinweis: Alle Arten von class_member_declarationaußer finalizer_declaration sind auch struct_member_declarations. Hinweisende
Mit Ausnahme der in §16.5 genannten Unterschiede gelten auch die Beschreibungen von Klassenmitgliedern gemäß §15.3 bis §15.12 auch für die Strukturmitglieder.
Es handelt sich um einen Fehler für ein Instanzfeld einer Datensatzstruktur, um einen unsicheren Typ zu haben.
16.3.2 Readonly-Mitglieder
Eine Instanzmemembedefinition oder ein Accessor einer Instanzeigenschaft, eines Indexers oder eines Ereignisses, das den readonly Modifizierer enthält, hat die folgenden Einschränkungen:
- Der
thisParameter ist einref readonlyVerweis. - Das Mitglied darf den Wert oder
thisein Instanzfeld des Empfängers nicht neu zuweisen. - Das Mitglied darf den Wert eines Instanzfeld-ähnlichen Ereignisses (§15.8.2) des Empfängers nicht neu zuweisen.
- Wenn ein readonly-Element ein nicht gelesenes Element aufruft, muss die struktur
this, auf die verwiesen wird, kopiert werden, um einen schreibbaren Verweis für dasthisArgument zu verwenden.
Hinweis: Instanzfelder enthalten das ausgeblendete Sicherungsfeld, das für automatisch implementierte Eigenschaften verwendet wird (§15.7.4). Hinweisende
Beispiel: Ein readonly-Element kann den Status eines Objekts ändern, auf das von einem Instanzfeld verwiesen wird, auch wenn das readonly-Element dieses Instanzmitglieds nicht neu zuweisen kann. Der folgende Code veranschaulicht das Neuzuweisen und Ändern eines Instanzfelds:
public struct S { private List<string> messages; public S(IEnumerable<string> messages) => this.messages = new List<string>(messages); public void InitializeMessages() => messages = new List<string>(); public readonly void AddMessage(string message) { if (messages == null) { throw new InvalidOperationException("Messages collection is not initialized."); } messages.Add(message); } }Die
readonlyMethodeAddMessagekann den Status einer Nachrichtenliste ändern. DasInitializeMessagesMitglied kann die Liste der Nachrichten löschen und erneut initialisieren. Im Fall vonAddMessage" ist derreadonlyModifizierer gültig. Im Fall vonInitializeMessages" ist das Hinzufügen desreadonlyModifizierers ungültig. Endbeispiel
16.4 Synthetisierte Datensatzstrukturmitglieder
16.4.1 Allgemein
Bei einer Datensatzstruktur werden Elemente synthetisiert, es sei denn, ein Element mit einer "matching"-Signatur wird im record_struct_body deklariert oder ein barrierefreies, nicht virtuelles Mitglied mit einer "matching"-Signatur geerbt. Zwei Member werden als übereinstimmend betrachtet, wenn sie dieselbe Signatur haben oder in einem Vererbungsszenario als "Ausblenden" betrachtet werden. (Siehe Signaturen und Überladung §7.6.)
Die synthetisierten Elemente werden in den folgenden Unterclauses beschrieben.
16.4.2 Gleichstellungsmitglieder
Die synthetisierten Gleichheitsmember ähneln denen für eine Datensatzklasse (§15.16.2), mit Ausnahme der fehlenden EqualityContractNullprüfungen oder Vererbung.
Eine Datensatzstruktur R implementiert System.IEquatable<R> und enthält eine synthetisierte stark typisierte Überladung von Equals(R other), die öffentlich ist, wie folgt:
public readonly bool Equals(R other);
Diese Methode kann explizit deklariert werden. Es handelt sich jedoch um einen Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Barrierefreiheit übereinstimmt.
Wenn Equals(R other) benutzerdefinierte (d. h. nicht synthetisiert) ist, wird GetHashCode jedoch eine Warnung erzeugt.
Der synthetisierte Equals(R) Wert muss nur zurückgegeben true werden, wenn für jedes Instanzfeld fieldN im Datensatz der Wert des System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)TN Feldtyps angegeben isttrue.
Die Datensatzstruktur enthält synthetisierte == Operatoren und != Operatoren, die wie folgt deklariert sind:
public static bool operator==(R r1, R r2) => r1.Equals(r2);
public static bool operator!=(R r1, R r2) => !(r1 == r2);
Die Equals vom == Operator aufgerufene Methode ist die Equals(R other) oben angegebene Methode. Der != Operator delegiert an den == Operator. Es ist ein Fehler, wenn die Operatoren explizit deklariert werden.
Die Datensatzstruktur enthält eine synthetisierte Überschreibung, die einer wie folgt deklarierten Methode entspricht:
public override readonly bool Equals(object? obj);
Es handelt sich um einen Fehler, wenn die Außerkraftsetzung explizit deklariert wird. Die synthetisierte Außerkraftsetzung gibt an other is R temp && Equals(temp) , wo R sich die Datensatzstruktur befindet.
Die Datensatzstruktur enthält eine synthetisierte Überschreibung, die einer wie folgt deklarierten Methode entspricht:
public override readonly int GetHashCode();
Diese Methode kann explizit deklariert werden.
Eine Warnung wird gemeldet, wenn eine von Equals(R) und GetHashCode() die andere Methode nicht explizit deklariert wird.
Die synthetisierte Außerkraftsetzung von GetHashCode() soll ein int Ergebnis zurückgeben, das die Werte für System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN) jedes Instanzfeld fieldN mit TN dem Typ von " fieldNkombiniert.
Beispiel: Betrachten Sie die folgende Datensatzstruktur:
record struct R1(T1 P1, T2 P2);Dazu würden die synthetisierten Gleichheitsmitglieder etwa wie folgt aussehen:
struct R1 : IEquatable<R1> { public T1 P1 { get; set; } public T2 P2 { get; set; } public override bool Equals(object? obj) => obj is R1 temp && Equals(temp); public bool Equals(R1 other) { return EqualityComparer<T1>.Default.Equals(P1, other.P1) && EqualityComparer<T2>.Default.Equals(P2, other.P2); } public static bool operator==(R1 r1, R1 r2) => r1.Equals(r2); public static bool operator!=(R1 r1, R1 r2) => !(r1 == r2); public override int GetHashCode() { return HashCode.Combine( EqualityComparer<T1>.Default.GetHashCode(P1), EqualityComparer<T2>.Default.GetHashCode(P2));Endbeispiel
16.4.3 Druckmitglieder
Eine Datensatzstruktur enthält eine synthetisierte Methode, die folgendem entspricht:
private bool PrintMembers(System.Text.StringBuilder builder);
Diese Methode führt die folgenden Aufgaben aus:
- Fügt für jede der druckbaren Member der Datensatzstruktur (nicht statische öffentliche Felder und lesbare Eigenschaftenmember) den Namen dieses Elements gefolgt von "
=" gefolgt vom Wert des Elements an, der durch ", “, - Gibt true zurück, wenn die Datensatzstruktur druckbare Elemente enthält.
Für ein Element mit einem Werttyp muss sein Wert in eine Zeichenfolgendarstellung konvertiert werden.
Wenn die druckbaren Elemente des Datensatzes keine lesbare Eigenschaft mit einem Nicht-Accessorreadonlyget enthalten, ist readonlydie Synthetisiert.PrintMembers Es ist nicht erforderlich, readonly dass die Felder des Datensatzes für die PrintMembers Methode gelten readonly.
Die PrintMembers Methode kann explizit deklariert werden. Es handelt sich jedoch um einen Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Barrierefreiheit übereinstimmt.
Die Datensatzstruktur enthält eine synthetisierte Methode, die folgendem entspricht:
public override string ToString();
Wenn die Methode der Datensatzstruktur PrintMembers lautet readonly, muss die synthetisierte ToString() Methode sein readonly.
Diese Methode kann explizit deklariert werden. Es ist ein Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Barrierefreiheit übereinstimmt.
Diese Methode führt die folgenden Aufgaben aus:
- Erstellt eine
StringBuilderInstanz, - Fügt den Namen der Datensatzstruktur an den Generator an, gefolgt von "
{", - Ruft die Methode der Datensatzstruktur
PrintMembersauf, die ihm den Generator gibt, gefolgt von "", wenn sie "true" zurückgegeben hat. - Fügt "
}", - Gibt den Inhalt des Generators mit
builder.ToString().
Beispiel: Betrachten Sie die folgende Datensatzstruktur:
record struct R1(T1 P1, T2 P2);Für diese Datensatzstruktur würden die synthetisierten Druckmitglieder etwa wie folgt aussehen:
struct R1 : IEquatable<R1> { public T1 P1 { get; set; } public T2 P2 { get; set; } private bool PrintMembers(StringBuilder builder) { builder.Append(nameof(P1)); builder.Append(" = "); builder.Append(this.P1); // or builder.Append(this.P1.ToString()); // if P1 has a value type builder.Append(", "); builder.Append(nameof(P2)); builder.Append(" = "); builder.Append(this.P2); // or builder.Append(this.P2.ToString()); // if P2 has a value type return true; } public override string ToString() { var builder = new StringBuilder(); builder.Append(nameof(R1)); builder.Append(" { "); if (PrintMembers(builder)) builder.Append(" "); builder.Append("}"); return builder.ToString(); } }Endbeispiel
16.4.4 Positionsdatensatz-Strukturmitglieder
16.4.4.1 Allgemein
Neben der Bereitstellung der in den vorstehenden Unterclauses beschriebenen Member synthetisieren Positionsdatensätze (§16.2.1) zusätzliche Member mit den gleichen Bedingungen wie die anderen Member, wie in den folgenden Unterclauses beschrieben.
16.4.4.2 Primärer Konstruktor
Eine Datensatzstruktur weist einen öffentlichen Konstruktor auf, dessen Signatur den Wertparametern der Typdeklaration entspricht. Dies wird als primärer Konstruktor für den Typ bezeichnet. Es ist ein Fehler, einen primären Konstruktor und einen Konstruktor mit derselben Signatur zu haben, die bereits in der Struktur vorhanden ist. Wenn die Typdeklaration keine delimited_parameter_list enthält, wird kein primärer Konstruktor generiert.
record struct R1 { public R1() { } // OK } record struct R2() { public R2() { } // error: 'R2' already defines // a constructor with the same parameter types }
Instanzenfelddeklarationen für eine Datensatzstruktur dürfen variable Initialisierer enthalten. Wenn kein primärer Konstruktor vorhanden ist, werden die Instanzinitialisierer als Teil des parameterlosen Konstruktors ausgeführt. Andernfalls führt der primäre Konstruktor zur Laufzeit die Instanzinitialisierer aus, die im Datensatz-Strukturtext angezeigt werden.
Wenn eine Datensatzstruktur über einen primären Konstruktor verfügt, muss jeder benutzerdefinierte Konstruktor einen expliziten this Konstruktorinitialisierer haben, der den primären Konstruktor oder einen explizit deklarierten Konstruktor aufruft.
Parameter des primären Konstruktors sowie Member der Datensatzstruktur sind innerhalb von Initialisierern von Instanzfeldern oder Eigenschaften im Bereich. Instanzmember wären an diesen Speicherorten ein Fehler, aber die Parameter des primären Konstruktors wären im Bereich und verwendbar und würden Schattenmember. Statische Elemente können ebenfalls verwendet werden.
Eine Warnung wird erzeugt, wenn ein Parameter des primären Konstruktors nicht gelesen wird.
Die endgültigen Zuordnungsregeln für Strukturinstanzkonstruktoren gelten für den primären Konstruktor von Datensatzstrukturen. Beispielsweise ist folgendes ein Fehler:
record struct Pos(int X) // def assignment error in primary constructor { private int x; public int X { get { return x; } set { x = value; } } = X; }
16.4.4.3 Eigenschaften
Für jeden Parameter eines delimited_parameter_list mit demselben Namen und Typ wie ein explizit deklariertes Instanzfeld gilt der Rest dieser Unterliste nicht.
Für jeden Datensatzstrukturparameter eines delimited_parameter_list gibt es ein entsprechendes öffentliches Eigenschaftselement, dessen Name und Typ aus der Wertparameterdeklaration entnommen werden.
Für eine Datensatzstruktur:
Eine öffentliche
getundinitautomatische Eigenschaft wird erstellt, wenn die Datensatzstruktur über einenreadonlyModifizierergetundsetandernfalls verfügt. Beide Arten von Set-Accessoren (setundinit) werden als "Abgleich" betrachtet. Daher kann der Benutzer anstelle einer synthetisierten änderbaren Eigenschaft eine init-only-Eigenschaft deklarieren.Eine geerbte
abstractEigenschaft mit übereinstimmendem Typ wird überschrieben.Es wird keine automatische Eigenschaft erstellt, wenn die Datensatzstruktur über ein Instanzfeld mit erwartetem Namen und Typ verfügt.
Es ist ein Fehler, wenn die geerbte Eigenschaft nicht über
publicgetundset/initAccessoren verfügt.Es handelt sich um einen Fehler, wenn die geerbte Eigenschaft oder das geerbte Feld ausgeblendet ist.
Die auto-Eigenschaft wird auf den Wert des entsprechenden primären Konstruktorparameters initialisiert.
Attribute können auf die synthetisierte auto-Eigenschaft und das zugehörige Sicherungsfeld angewendet werden, indem
property:Attribute syntaktisch auf den entsprechenden Datensatzstrukturparameter angewendet werdenfield:.
16.4.4.4 Dekonstruktur
Eine Positionsdatensatzstruktur mit mindestens einem Parameter synthetisiert eine öffentliche voidInstanzmethode, die mit einer Ausgabeparameterdeklaration für jeden Parameter der primären Konstruktordeklaration aufgerufen wird Deconstruct . Jeder Parameter hat Deconstruct denselben Typ wie der entsprechende Parameter der primären Konstruktordeklaration. Der Textkörper der Methode weist jedem Parameter der Deconstruct-Methode den Wert von einem Instanzmemembzugriff auf ein Element mit demselben Namen zu.
Wenn die Instanzmember, auf die im Textkörper zugegriffen wird, keine Eigenschaft mit einem Nicht-Accessorreadonlyget enthalten, lautet readonlydie synthetisierte Deconstruct Methode .
Die Methode kann explizit deklariert werden. Es ist ein Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Barrierefreiheit übereinstimmt oder statisch ist.
16.5 Klassen- und Strukturunterschiede
16.5.1 Allgemein
Die Struktur unterscheidet sich von Klassen auf verschiedene wichtige Arten:
- Strukturen sind Werttypen (§16.5.2).
- Alle Strukturtypen erben implizit von der Klasse
System.ValueType(§16.5.3). - Die Zuordnung zu einer Variablen eines Strukturtyps erstellt eine Kopie des zugewiesenen Werts (§16.5.4).
- Der Standardwert einer Struktur ist der Wert, der erzeugt wird, indem alle Felder auf ihren Standardwert festgelegt werden (§16.5.5).
- Boxing- und Unboxing-Vorgänge werden verwendet, um zwischen einem Strukturtyp und bestimmten Verweistypen (§16.5.6) zu konvertieren.
- Die Bedeutung unterscheidet sich innerhalb der
thisStrukturmitglieder (§16.5.7). - Eine Struktur darf keinen Finalizer deklarieren.
- Ereignisdeklarationen, Eigenschaftsdeklarationen, Eigenschaftenaccessoren, Indexerdeklarationen und Methodendeklarationen dürfen den Modifizierer
readonlyaufweisen, während dies für dieselben Membertypen in Klassen nicht allgemein zulässig ist.
16.5.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 Xhängt direkt von einer Struktur Y ab, wenn X ein Instanzfeld vom Typ Yenthä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
Nodeein 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,BundCvon 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 vonazuberstellt eine Kopie des Werts, undbist somit von der Zuordnung zua.xunberührt. WärePointstattdessen als Klasse deklariert worden, wäre die Ausgabe100, weilaundbauf dasselbe Objekt verweisen würden.Endbeispiel
16.5.3 Vererbung
Alle Strukturtypen erben implizit von der Klasse System.ValueType, die wiederum von der Klasse objecterbt. 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 Barrierefreiheit eines Strukturelements nicht protectedsein , private protectedoder protected internal.
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.5.4 Aufgabe
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.24.2 ausführlich beschrieben.
16.5.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 nulldieser Standardwert . Da Structs jedoch Werttypen sind, die nicht sein nullkö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
Pointoben deklarierte Struktur, das BeispielPoint[] a = new Point[100];initialisiert jedes Element im Array
Pointauf den Wert, der durch Setzen der Felderxundyauf Null erzeugt wird.Endbeispiel
Der Standardwert einer Struktur entspricht dem Wert, der vom Standardkonstruktor der Struktur zurückgegeben wird (§8.3.3). Wenn eine Struktur keinen expliziten parameterlosen Instanzkonstruktor deklariert, wird der Standardkonstruktor synthetisiert und gibt immer den Wert zurück, der daraus resultiert, dass alle Felder auf ihre Standardwerte festgelegt werden. Der default Ausdruck erzeugt immer den null initialisierten Standardwert, auch wenn eine Struktur einen expliziten parameterlosen Instanzkonstruktor deklariert (§16.4.9).
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
nullWerten, wenn er explizit aufgerufen wird. In Fällen, in denen eineKeyValuePairVariable der Standardwertinitialisierung unterliegt, sind diekeyvalueFeldernull, und die Struktur sollte darauf vorbereitet sein, diesen Zustand zu verarbeiten.Hinweisende
16.5.6 Boxen und Entboxen
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
structvorgenommen wurden, nicht in dem boxed wiedergegebenstruct. Hinweisende
Weitere Informationen zum Boxen und Entpacken finden Sie unter §10.2.9 und §10.3.7.
16.5.7 Bedeutung
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, GetHashCodeoder 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 3Obwohl es schlechter Stil ist, wenn
ToStringNebenwirkungen 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 1Endbeispiel
16.5.8 Feldinitialisierer
Wie in §16.5.5 beschrieben, besteht der Standardwert einer Struktur aus dem Wert, der sich aus dem Festlegen aller Werttypfelder auf den Standardwert und alle Bezugstypfelder ergibt.null Statische Felder und Instanzen einer Struktur dürfen variable Initialisierer enthalten; Bei einem Instanzfeldinitialisierer muss jedoch mindestens ein Instanzkonstruktor ebenfalls deklariert oder für eine Datensatzstruktur ein delimited_parameter_list vorhanden sein.
Beispiel:
Console.WriteLine($"Point is {new Point()}"); struct Point { public int x = 1; public int y = 1; public Point() { } public override string ToString() { return "(" + x + ", " + y + ")"; } }Point is (1, 1)Endbeispiel
Wenn ein Strukturinstanzkonstruktor keinen Konstruktorinitialisierer hat, führt dieser Konstruktor implizit die initialisierungen aus, die von den variable_initializerder Instanzfelder angegeben werden, die in der zugehörigen Struktur deklariert sind. Dies entspricht einer Abfolge von Zuordnungen, die unmittelbar nach dem Eintrag zum Konstruktor ausgeführt werden.
Wenn ein Strukturinstanzkonstruktor über einen this() Konstruktorinitialisierer verfügt, der den standardparameterlosen Konstruktor darstellt, löscht der deklarierte Konstruktor implizit alle Instanzfelder und führt die initialisierungen aus, die von den variable_initializers der Instanzfelder in seiner Struktur deklariert wurden. Unmittelbar nach dem Eintrag in den Konstruktor werden alle Werttypfelder auf ihren Standardwert festgelegt, und alle Bezugstypfelder werden auf nullfestgelegt. Unmittelbar danach werden eine Abfolge von Zuordnungen ausgeführt, die den variable_initializers entsprechen.
Eine field_declaration , die direkt innerhalb einer struct_declaration mit dem struct_modifierreadonly deklariert wird, hat die field_modifierreadonly.
16.5.9 Konstruktoren
Eine Struktur kann Instanzkonstruktoren mit null oder mehr Parametern deklarieren. Wenn eine Struktur keinen explizit deklarierten Parameterlosen Instanzkonstruktor aufweist, wird ein Konstruktor mit öffentlicher Barrierefreiheit synthetisiert, wodurch immer der Wert zurückgegeben wird, der aus dem Festlegen aller Werttypfelder auf den Standardwert und alle Verweistypfelder (null§8.3.3) resultiert. In diesem Fall werden alle Instanzfeldinitialisierer ignoriert, wenn dieser Konstruktor ausgeführt wird.
Ein explizit deklarierter parameterloser Instanzkonstruktor muss über öffentliche Barrierefreiheit verfügen.
Beispiel: In Anbetracht der folgenden Punkte:
using System; struct Point { int x = -1, y = -2; public Point(int x, int y) { this.x = x; this.y = y; } public override string ToString() { return "(" + x + ", " + y + ")"; } } class A { static void Main() { Console.WriteLine($"Point is {new Point()}"); Console.WriteLine($"Point is {new Point(0,0)}"); } }Point is (0, 0) Point is (0, 0)Die Anweisungen erstellen sowohl ein Mit-
Pointxalsyauch initialisiert auf Null, was im Fall des Aufrufs des parameterlosen Instanzkonstruktors überraschend sein kann, da beide Instanzfelder Initialisierer haben, aber nicht ausgeführt werden.Endbeispiel
Ein Strukturinstanzkonstruktor darf keinen Konstruktorinitialisierer des Formulars base(argument_list) enthalten, wobei argument_list optional ist. Die Ausführung eines Instanzkonstruktors führt nicht zur Ausführung eines Konstruktors im Basistyp System.ValueTypeder Struktur.
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.
Instanzfelder (außer fixed Feldern) werden definitiv in Strukturinstanzkonstruktoren zugewiesen, die keinen Initialisierer besitzen this() .
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
XundY) kann aufgerufen werden, bis alle Felder der zu erstellenden Struktur definitiv zugewiesen wurden. Beachten Sie jedoch, dass beiPointeiner 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 eindeutigen Zuordnungsregeln (§12.24.2) ausgenommen die Zuordnung zu einer automatischen Eigenschaft eines Strukturtyps innerhalb eines Instanzkonstruktors dieses Strukturtyps: Eine solche Zuordnung gilt als eine bestimmte Zuordnung des ausgeblendeten Sicherungsfelds der auto-Eigenschaft. 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.5.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.5.5) von Strukturtypen löst den statischen Konstruktor nicht aus. (Ein Beispiel hierfür ist der Anfangswert von Elementen in einem Array.) Endnote
16.5.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
Für einen Eigenschaftsaccessorausdruck: s.P
- Es handelt sich um einen Kompilierzeitfehler, wenn der Set-Accessor
s.Pdes TypsMaufgerufen wird, wenn der Prozess inTeine temporäre Kopie von .s - Wenn
s.Pder Get-Accessor vom TypTaufgerufen wird, wird dem Prozess in §12.6.6.1 gefolgt, einschließlich der Erstellung einer temporären Kopie, fallsserforderlich.
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.5.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.5.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.5.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.
16.5.15 Einschränkung für sichere Kontexte
16.5.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 e1muss der sichere Kontext vone1der Aufrufer-Kontext sein. - Für eine Zuordnung
e1 = e2muss der sichere Kontexte2mindestens 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.5.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.5.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
foreachSchleife handelt, entspricht der sichere Kontext der Variablen dem sicheren Kontext des Ausdrucks derforeachSchleife. - 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.5.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.5.15.5 Betreiber
Die Anwendung eines benutzerdefinierten Operators wird als Methodenaufruf behandelt (§16.5.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.5.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.5.15.7 stackalloc
Das Ergebnis eines stackalloc-Ausdrucks hat den sicheren Kontext von function-member.
16.5.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 einemrefFeld nicht unterscheidbar. Die in diesem Dokument beschriebenen Sicherheitsregeln hängen davon ab,refFelder kein gültiges Konstrukt in C# oder .NET sind. Hinweisende
ECMA C# draft specification