Freigeben über


ref-Strukturtypen (C#-Referenz)

Sie können bei der Deklaration eines Strukturtyps den ref-Modifizierer verwenden. Instanzen eines ref struct-Typs werden auf dem Stapel zugeordnet und können nicht in den verwalteten Heap überwechseln. Der Compiler grenzt die Nutzung von ref struct-Typen wie folgt ein, um dies sicherzustellen:

  • Eine ref struct kann nicht der Elementtyp eines Arrays sein.
  • Eine ref struct kann kein deklarierter Typ eines Felds einer Klasse oder einer ref struct-fremden Struktur sein.
  • Für eine ref struct kann kein Boxing in System.ValueType oder System.Object durchgeführt werden.
  • Eine ref struct-Variable kann nicht in einem Lambdaausdruck oder einer lokalen Funktion erfasst werden.
  • Vor C# 13 ref struct können Variablen nicht in einer async Methode verwendet werden. Ab C# 13 kann eine ref struct-Variable nicht im selben Block wie der await-Ausdruck in einer async-Methode verwendet werden. Sie können ref struct-Variablen jedoch in synchronen Methoden verwenden, z. B. in solchen, die Task oder Task<TResult> zurückgeben.
  • Vor C# 13 kann eine ref struct-Variable kann nicht in Iteratoren verwendet werden. Ab C# 13 können ref struct-Typen und lokale ref-Variablen in Iteratoren verwendet werden, vorausgesetzt, sie befinden sich nicht in Codesegmenten mit der yield return-Anweisung.
  • Vor C# 13 kann eine ref struct keine Schnittstellen implementieren. Ab C# 13 kann eine ref-Struktur Schnittstellen implementieren, muss jedoch den Regeln für die Verweissicherheit folgen. Beispielsweise kann ein ref struct-Typ nicht in den Schnittstellentyp konvertiert werden, da dies eine Boxing-Konvertierung erfordert.
  • Vor C# 13 kann eine ref struct kein Typargument sein. Ab C# 13 kann eine ref struct das Typargument sein, wenn der Typparameter die allows ref struct in der where-Klausel angibt.

In der Regel definieren Sie einen ref struct-Typ, wenn Sie einen Typ benötigen, der außerdem Datenmember von ref struct-Typen enthält:

public ref struct CustomRef
{
    public bool IsValid;
    public Span<int> Inputs;
    public Span<int> Outputs;
}

Kombinieren Sie die readonly- und ref-Modifizierer in der Typdeklaration (der readonly-Modifizierer muss vor dem ref-Modifizierer stehen), um eine ref struct als readonly zu deklarieren:

public readonly ref struct ConversionRequest
{
    public ConversionRequest(double rate, ReadOnlySpan<double> values)
    {
        Rate = rate;
        Values = values;
    }

    public double Rate { get; }
    public ReadOnlySpan<double> Values { get; }
}

In .NET sind System.Span<T> und System.ReadOnlySpan<T> Beispiele für eine ref struct.

ref-Felder

Ab C# 11 können Sie ein ref-Feld in einer ref struct deklarieren, wie im folgenden Beispiel gezeigt:

public ref struct RefFieldExample
{
    private ref int number;

    public int GetNumber()
    {
        if (System.Runtime.CompilerServices.Unsafe.IsNullRef(ref number))
        {
            throw new InvalidOperationException("The number ref field is not initialized.");
        }

        return number;
    }
}

Ein ref-Feld kann den Wert null aufweisen. Verwenden Sie die Unsafe.IsNullRef<T>(T)-Methode, um zu bestimmen, ob ein ref-Feld null ist.

Sie können den readonly-Modifizierer wie folgt auf ein ref-Feld anwenden:

  • readonly ref: Sie können ein solches Feld mit dem = ref-Operator nur innerhalb eines Konstruktors oder einer init-Zugriffsmethodeneu als Verweis zuweisen. Sie können mit dem =-Operator an jedem beliebigen Punkt einen Wert zuweisen, der durch den Feldzugriffsmodifizierer zugelassen wird.
  • ref readonly: An keinem Punkt können Sie einem solchen Feld einen Wert mit dem =-Operator zuweisen. Jedoch können Sie ein Feld mit dem = ref-Operator als Verweis erneut zuweisen.
  • readonly ref readonly: Sie können ein solches Feld nur in einem Konstruktor oder einer init-Zugriffsmethode als Verweis erneut zuweisen. An keinem Punkt können Sie dem Feld einen Wert zuweisen.

Der Compiler stellt sicher, dass ein in einem ref-Feld gespeicherter Verweis nicht länger vorhanden ist als das Element, auf das er verweist.

Das Feldfeature ref ermöglicht eine sichere Implementierung von Typen wie System.Span<T>:

public readonly ref struct Span<T>
{
    internal readonly ref T _reference;
    private readonly int _length;

    // Omitted for brevity...
}

Der Span<T>-Typ speichert einen Verweis, über den er auf die zusammenhängenden Elemente im Arbeitsspeicher zugreift. Durch die Verwendung eines Verweises muss eine Span<T>-Instanz den Speicher, auf den sie verweist, nicht kopieren.

Das verwerfbare Muster

Sie können eine verwerfbare ref structdefinieren. Stellen Sie dazu sicher, dass eine ref struct dem Dispose-Muster entspricht. Das heißt, sie umfasst eine Instanzmethode Dispose, auf die zugegriffen werden kann, die parameterlos ist und einen Rückgabetyp void aufweist. Sie können die using-Anweisung oder -Deklaration mit einer Instanz einer verwerfbaren ref struct verwenden.

Ab C# 13 können Sie die IDisposable auch für ref struct-Typen implementieren. Die Überladungsauflösung bevorzugt jedoch das verwerfbare Muster gegenüber der Schnittstellenmethode. Der Compiler wird nur dann in eine IDisposable.Dispose Methode aufgelöst, wenn eine geeignete Dispose Methode nicht gefunden wird.

Einschränkungen für ref struct-Typen, die eine Schnittstelle implementieren

Diese Einschränkungen stellen sicher, dass ein ref struct-Typ, der eine Schnittstelle implementiert, den erforderlichen Regeln für die Verweissicherheit entspricht.

  • Eine ref struct kann nicht in eine Instanz einer von ihr implementierten Schnittstelle konvertiert werden. Diese Einschränkung umfasst die implizite Konvertierung, wenn Sie einen ref struct-Typ als Argument verwenden, wenn der Parameter ein Schnittstellentyp ist. Die Konvertierung führt zu einer Boxing-Konvertierung, die gegen die Verweissicherheit verstößt.
  • Eine ref struct, die eine Schnittstelle implementiert, muss alle Schnittstellenmitglieder implementieren. Die ref struct muss Mitglieder implementieren, in denen die Schnittstelle eine Standardimplementierung enthält.

Der Compiler erzwingt diese Einschränkungen. Wenn Sie ref struct-Typen schreiben, die Schnittstellen implementieren, enthält jedes neue Update möglicherweise neue Standardschnittstellenmitglieder. Ihre Anwendung wird erst kompiliert, wenn Sie eine Implementierung für diese neuen Methoden bereitstellen.

Wichtig

Eine ref struct, die eine Schnittstelle implementiert, kann später zu Änderungen führen, die den Quell- und Binärcode beeinträchtigen (Breaking Change). Dieser Fall tritt ein, wenn eine ref struct eine in einer anderen Assembly definierte Schnittstelle implementiert und diese Assembly ein Update bereitstellt, das dieser Schnittstelle Standardmitglieder hinzufügt.

Der Breaking Change am Quellcode erfolgt, wenn Sie die ref struct neu kompilieren: Sie muss das neue Mitglied implementieren, obwohl eine Standardimplementierung vorhanden ist.

Der Breaking Change am Binärcode erfolgt, wenn Sie die externe Assembly aktualisieren, ohne den ref struct-Typ neu zu kompilieren, und der aktualisierte Code die Standardimplementierung der neuen Methode aufruft. Die Runtime löst eine Ausnahme aus, wenn auf das Standardmitglied zugegriffen wird.

C#-Sprachspezifikation

Weitere Informationen finden Sie in den folgenden Abschnitten der C#-Sprachspezifikation:

Weitere Informationen zu ref-Feldern finden Sie im Vorschlag zu Strukturverbesserungen auf niedriger Ebene.

Weitere Informationen