Freigeben über


16 Strukturen

16.1 Allgemein

Strukturen ähneln Klassen, in denen sie Datenstrukturen darstellen, die Datenmmber und Funktionsmmber enthalten können. Im Gegensatz zu Klassen sind Strukturtypen jedoch Werttypen und erfordern keine Heapzuordnung. 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: Strukturs 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. Endnote

Wie in §8.3.5 beschrieben, sind die einfachen Typen, die von C# bereitgestellt werden, z int. B. , doubleund 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 ';'?
    ;

Ein struct_declaration besteht aus einem optionalen Satz von Attributen (§22), 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.

Eine Strukturerklärung stellt keine type_parameter_constraints_clauses zur Verfügung , es sei denn, sie liefert auch eine type_parameter_list.

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

Ein struct_declaration kann optional eine Abfolge von struct_modifiereinschließen:

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.

readonlyMit Ausnahme der Modifizierer einer Strukturdeklaration hat 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.
  • Keiner seiner Instanzeigenschaften hat eine set_accessor_declaration (§15.7.3).
  • Sie darf keine feldähnlichen Ereignisse (§15.8.2) deklarieren.

Wenn eine Instanz einer schreibgeschützten Struktur an eine Methode übergeben wird, wird sie this wie ein Eingabeargument/-parameter behandelt, wodurch schreibgeschützter Zugriff auf alle Instanzfelder (mit Ausnahme von Konstruktoren) nicht zulässig ist.

16.2.3 Refmodifizierer

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.12) kopiert werden. Die Regeln zur Bestimmung des sicheren Kontexts einer Refstruktur werden in §16.4.12 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.
  • Wird eingeboxt in System.ValueType oder System.Object.
  • Als Typargument.
  • Als Typ eines Tupelelements.
  • Eine asynchrone Methode.
  • Ein Iterator.
  • Es gibt keine Konvertierung von einem ref struct Typ in den Typ object oder den Typ System.ValueType.
  • Ein ref struct Typ darf nicht deklariert werden, um eine Schnittstelle zu implementieren.
  • Eine Instanzmethode, die in object oder in System.ValueType einem ref struct Typ deklariert oder nicht außer Kraft gesetzt wird, darf nicht mit einem Empfänger dieses ref struct Typs aufgerufen werden.
  • Eine Instanzmethode eines ref struct Typs darf nicht von der Methodengruppenkonvertierung in einen Delegattyp erfasst werden.
  • Eine Verweisstruktur darf nicht von einem Lambda-Ausdruck oder einer lokalen Funktion erfasst werden.

Hinweis: A ref struct deklariert weder async Instanzmethoden noch eine yield return yield break Anweisung innerhalb einer Instanzmethode, da der implizite this Parameter in diesen Kontexten nicht verwendet werden kann. Endnote

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 in einem eingeschlossenen Namespace oder einer Typdeklaration kombinieren eine Strukturdeklaration nach den in §15.2.7 angegebenen Regeln.

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 Strukturtext

Die struct_body einer Struktur definiert die Member der Struktur.

struct_body
    : '{' struct_member_declaration* '}'
    ;

16.3 Strukturmitglieder

Die Member einer Struktur bestehen aus den Elementen, die durch die struct_member_declarations und die vom Typ System.ValueTypegeerbten Member eingeführt wurden.

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

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-Vorgänge werden verwendet, um zwischen einem Strukturtyp und bestimmten Bezugstypen (§16.4.6) zu konvertieren.
  • Die Bedeutung unterscheidet sich innerhalb von this Strukturmitgliedern (§16.4.7).
  • 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.

16.4.2 Wertsemantik

Structs sind Werttypen (§8.3) und werden als Wertsemantik bezeichnet. 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, handelt es sich um einen Kompilierungszeitfehler, der A von B einem Typ abhängig oder von einem Typ erstellt wird B. A struct Xhängt direkt von einer Struktur Y ab, wenn X ein Instanzfeld vom Typ Yenthält. In Anbetracht dieser Definition hängt der vollständige Satz von Strukturen, von denen eine Struktur abhängt, die transitive Schließung der direkt von der Beziehung ab .

Beispiel:

struct Node
{
    int data;
    Node next; // error, Node directly depends on itself
}

ist ein Fehler, da Node ein Instanzfeld des eigenen Typs enthalten ist. Ein weiteres Beispiel

struct A { B b; }
struct B { C c; }
struct C { A a; }

ist ein Fehler, da jeder der Typen A, Bund C von einander abhängig ist.

Endbeispiel

Bei Klassen ist es möglich, dass zwei Variablen auf dasselbe Objekt verweisen, und somit für Vorgänge auf eine Variable, die auf das objekt verweist, auf das von der anderen Variable verwiesen wird. Bei Strukturierungen verfügen die Variablen jeweils über eine eigene Kopie der Daten (mit Ausnahme von By-Reference-Parametern), und es ist nicht möglich, vorgänge auf eins zu wirken. Außer wenn explizit Nullwerte (§8.3.12) zulässig sind, ist es für Werte eines Strukturtyps nullnicht möglich.

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

Beispiel: Im Folgenden

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 der a zu b erstellenden Kopie des Werts und b ist somit von der Zuordnung a.xunberührt. Wäre Point stattdessen als Klasse deklariert worden, würde die Ausgabe darauf a zurückzuführen sein 100 und b auf dasselbe Objekt verweisen.

Endbeispiel

16.4.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 abstract Modifizierer und sealed Modifizierer sind daher in einer Strukturdeklaration nicht zulässig.

Da die Vererbung für Structs nicht unterstützt wird, kann die deklarierte Barrierefreiheit eines Strukturmememers nicht protected, oder private protectedprotected internal.

Funktionsmember in einer Struktur können nicht abstrahiert oder virtuell sein, und der override Modifizierer darf nur Methoden außer Kraft setzen, die System.ValueTypevon .

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 Kompilierungszeitfehler 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 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 Point oben deklarierte Struktur, das Beispiel

Point[] a = new Point[100];

initialisiert jedes Point Array auf den Wert, der durch Festlegen der x Felder y 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 eine KeyValuePair Variable der Standardwertinitialisierung unterliegt, sind nulldie key Felder value und felder, und die Struktur sollte für die Verarbeitung dieses Zustands vorbereitet sein.

Endnote

16.4.6 Boxen und Entpacken

Ein Wert eines Klassentyps kann in Typ object oder in einen Schnittstellentyp konvertiert werden, der von der Klasse implementiert wird, indem der Verweis zur Kompilierungszeit einfach als ein anderer Typ behandelt 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. Wenn ein Wert bestimmter Bezugstypen (wie in §10.3.7 definiert) in einen Strukturtyp konvertiert wird, erfolgt ein Unboxing-Vorgang. Ein wichtiger Unterschied zu den gleichen Vorgängen bei Klassentypen besteht darin, dass das Boxen und Aufheben des Posteingangs den Strukturwert entweder in die oder aus der boxenden Instanz kopiert .

Hinweis: Nach einem Box- oder Unboxing-Vorgang werden änderungen, die an dem Unboxed struct vorgenommen wurden, nicht im Boxen widergespiegelt struct. Endnote

Weitere Informationen zum Boxen und Entpacken finden Sie unter §10.2.9 und §10.3.7.

16.4.7 Bedeutung

Die Bedeutung einer this Struktur unterscheidet sich von der Bedeutung einer this Klasse, wie in §12.8.14 beschrieben. Wenn ein Strukturtyp eine virtuelle Methode außer Kraft setzt, die von System.ValueType (z Equals. B. , GetHashCode, oder ToString), aufruft die virtuelle Methode über eine Instanz des Strukturtyps geerbt wird, führt nicht dazu, dass boxen. 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 lautet:

1
2
3

Obwohl es ein schlechter Stil ist, um ToString Nebenwirkungen zu haben, zeigt das Beispiel, dass für die drei Aufrufe kein x.ToString()Boxen aufgetreten ist.

Endbeispiel

Ebenso tritt das Boxen niemals implizit auf, wenn auf ein Element für einen eingeschränkten Typparameter zugegriffen wird, wenn das Element innerhalb des Werttyps implementiert wird. Angenommen, eine Schnittstelle ICounter enthält eine Methode Increment, die zum Ändern eines Werts verwendet werden kann. Wenn ICounter sie als Einschränkung verwendet wird, wird die Implementierung der Increment Methode mit einem Verweis auf die Variable aufgerufen, die Increment aufgerufen wurde, niemals eine Boxkopie.

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, um den Wert in der Variablen xzu Increment ändern. Dies entspricht nicht dem zweiten Aufruf Increment, der den Wert in einer boxierten Kopie von x. So ist die Ausgabe des Programms:

0
1
1

Endbeispiel

16.4.8 Feldinitialisierer

Wie in §16.4.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 Aus diesem Grund lässt eine Struktur keine Instanzfelddeklarationen zu, variablen Initialisierer einzuschließen. Diese Einschränkung gilt nur für Instanzfelder. Statische Felder einer Struktur dürfen variable Initialisierer enthalten.

Beispiel: Die folgenden

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

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: Im Folgenden

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 eine Point mit x und y 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. Daher ist an jedem Ort, an dem der Konstruktor zurückkehrt, this definitiv (§9.4) zugewiesen. Ebenso kann sie nicht (auch implizit) im Konstruktortext gelesen werden, bevor sie auf jeden Fall zugewiesen wird.

Wenn der Strukturinstanzkonstruktor einen Konstruktorinitialisierer angibt, wird dieser Initialisierer als eine bestimmte Zuordnung zu dieser vor dem Textkörper des Konstruktors betrachtet. Daher hat der Textkö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
    }
}

Es kann kein Instanzfunktionsmemmm (einschließlich der Satzaccessoren für die Eigenschaften X ) Yaufgerufen werden, bis alle Felder der zu erstellenden Struktur definitiv zugewiesen wurden. Beachten Sie jedoch, dass bei Point 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 endgültigen Zuordnungsregeln (§12.21.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.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 Automatisch implementierte Eigenschaften

Automatisch implementierte Eigenschaften (§15.7.4) verwenden ausgeblendete Sicherungsfelder, auf die nur für die Eigenschaftenaccessoren zugegriffen werden kann.

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

16.4.12 Einschränkung sicherer Kontext

16.4.12.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 einschließt, der sicher ist, dass der Wert escape ist.

Jeder Ausdruck, dessen Kompilierungszeittyp keine Referenzstruktur ist, weist einen sicheren Kontext des Aufruferskontexts auf.

Ein default Ausdruck hat für jeden Typ einen sicheren Kontext des Aufruferskontexts.

Für alle nicht standardmäßigen Ausdrücke, deren Kompilierungszeittyp eine Referenzstruktur ist, weist einen sicheren Kontext auf, der in den folgenden Abschnitten definiert ist.

Die Safe-Context-Datensätze, in die ein Wert kopiert werden kann. Wenn eine Zuordnung von einem Ausdruck E1 mit einem sicheren Kontext S1zu einem Ausdruck E2 mit sicherem Kontext S2erfolgt, handelt es sich um einen Fehler, wenn S2 es sich um einen breiteren Kontext handelt als S1.

Es gibt drei verschiedene Werte für sichere Kontexte, die mit den für Referenzvariablen definierten Referenzvariablen (§9.7.2) identisch sind: Deklarationsblock, Funktionsmemembe und Aufruferkontext. Der sichere Kontext eines Ausdrucks schränkt die Verwendung wie folgt ein:

  • Für eine Rückgabeanweisung return e1muss der sichere Kontext e1 des Aufrufers verwendet werden.
  • Für eine Zuordnung e1 = e2 muss der sichere Kontext e2 mindestens so breit wie der sichere Kontext sein e1.

Bei einem Methodenaufruf, wenn ein ref out Oder-Argument eines ref struct Typs vorhanden ist (einschließlich des Empfängers, es sei denn, der Typ ist readonly), mit sicherem Kontext S1, kann kein Argument (einschließlich des Empfängers) einen schmaleren sicheren Kontext aufweisen als S1.

16.4.12.2 Parameter sicherer Kontext

Ein Parameter eines Verweisstrukturtyps, einschließlich des this Parameters einer Instanzmethode, weist einen sicheren Kontext des Aufruferskontexts auf.

16.4.12.3 Lokaler sicherer Kontext für Variable

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 der foreach Schleife.
  • Andernfalls entspricht die Deklaration der Variablen einem Initialisierer, der sicheren Kontext der Variablen dem sicheren Kontext dieses Initialisierers entspricht.
  • Andernfalls wird die Variable am Deklarationspunkt nicht initialisiert und verfügt über einen sicheren Kontext des Aufruferskontexts.

16.4.12.4 Feldsicherer Kontext

Ein Verweis auf ein Feld e.F, bei dem der Typ der F Refstruktur einen sicheren Kontext aufweist, der dem sicheren Kontext eentspricht.

16.4.12.5 Operatoren

Die Anwendung eines benutzerdefinierten Operators wird als Methodenaufruf behandelt (§16.4.12.6).

Bei einem Operator, der einen Wert wie e1 + e2 oder c ? e1 : e2einen sicheren Kontext des Ergebnisses zurückgibt, ist der schmalste Kontext zwischen den sicheren Kontexten der Operanden des Operators. Folglich ist für einen unären Operator, der einen Wert wie den +esicheren Kontext des Ergebnisses zurückgibt, der sichere Kontext des Ergebnisses der sichere Kontext des Operanden.

Hinweis: Der erste Operand eines bedingten Operators ist ein bool, sodass der sichere Kontext der Aufruferkontext ist. Es folgt, dass der resultierende sichere Kontext der schmalste sichere Kontext des zweiten und dritten Operanden ist. Endnote

16.4.12.6 Methode und Eigenschaftsaufruf

Ein Wert, der sich aus einem Methodenaufruf oder einem Eigenschaftsaufruf e1.M(e2, ...) e.P ergibt, weist einen sicheren Kontext der kleinsten der folgenden Kontexte auf:

  • caller-context.
  • 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.12.7 stackalloc

Das Ergebnis eines Stackalloc-Ausdrucks weist den sicheren Kontext von Funktionsmememm auf.

16.4.12.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 Instanzen verwendet, die Span<T> als Felder nicht von einem ref Feld unterscheiden können. Die in diesem Dokument beschriebenen Sicherheitsregeln hängen davon ab ref , dass Felder kein gültiges Konstrukt in C# oder .NET sind. Endnote