你可以在 struct 的聲明中使用 ref 修飾符。ref struct 類型的實例是在堆棧上分配的,不能轉義到託管堆。為了確保這一點,編譯器將 ref struct 類型的使用限制如下:
- ref struct 不能是數組的元素類型。
- ref struct 不能是類或非 ref struct 的字段的聲明類型。
- ref struct 不能被 boxed 為 System . ValueType 或 System . Object。
- ref struct 變量不能在 Lambda 表達式或本地函數中捕獲。
- 在 C# 13 之前, ref struct 無法在方法中使用 async 變量。從 C# 13 開始,ref struct 變量不能在 await 方法中和 async 表達式用於相同的塊。但是,可以在同步方法中使用 ref struct 變量,例如,在返回 Task 或 Task < TResult > 的方法中。
- 在 C# 13 之前,ref struct 變量不能在迭代器中使用。從 C# 13 開始,ref struct 類型和 ref 局部變量可以在迭代器中使用,前提是它們不在代碼段中具有 yield return 語句。
- 在 C# 13 之前,ref struct 無法實現接口。從 C# 13 開始,ref 結構可實現接口,但必須遵循 ref 安全性規則。例如,由於需要裝箱轉換,因此無法將 ref struct 類型轉換為接口類型。
- 在 C# 13 之前,ref struct 不能是類型參數。從 C# 13 開始,ref struct 可以是類型參數(當該類型參數在其 allows ref struct 子句中指定 where 時)。
通常,如果需要一種同時包含 ref struct 類型的數據成員的類型,可以定義 ref struct 類型:
public ref struct CustomRef
{
public bool IsValid;
public Span < int > Inputs;
public Span < int > Outputs;
}
若要將 ref struct 聲明為 readonly,請在類型聲明中組合使用 readonly 修飾符和 ref 修飾符(readonly 修飾符必須位於 ref 修飾符之前):
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; }
}
在 .NET 中,ref struct 的示例分別是 System . Span < T > 和 System . ReadOnlySpan < T >。
ref 字段
從 C# 11 開始,可以在 ref 中聲明 ref struct 字段,如以下示例所示:
public ref struct RefFieldExample
{
private ref int number;
public int GetNumber ( )
{
if ( System . Runtime . CompilerServices . Unsafe . IsNullRef ( ref number ) )
{
throw new InvalidOperationException ( "編號引用字段未初始化。");
}
return number;
}
}
ref 字段可以具有 null 值。使用 Unsafe . IsNullRef < T > ( T ) 方法確定 ref 字段是否為 null。
可通過以下方式將 readonly 修飾符應用於 ref 字段:
- readonly ref:只能在構造函數或初始化訪問器內部使用
= ref操作符重新分配此類字段。可以在字段訪問修飾符允許的任何位置使用=操作符分配值。 - ref readonly:在任何時候,都不能使用 = 運算符為此類字段賦值。但是,可以使用 = ref 運算符通過 ref 重新賦值字段。
- readonly ref readonly:只能在構造函數或 init 訪問器中通過 ref 重新賦值此類字段。在任何時候,都不能為字段賦值。
編譯器會確保存儲在 “ref” 字段中的引用不會超出其所引用對象的生存期。
ref 字段功能支持安全實現類型,例如 System . Span < T >:
public readonly ref struct Span < T >
{
internal readonly ref T _reference;
private readonly int _length;
// Omitted for brevity……
}
Span < T > 類型存儲一個引用,通過該引用訪問內存中的連續元素。通過使用引用,Span < T > 實例可以避免複製它所引用的存儲。
可釋放模式
可以定義一次性的 ref struct。為此,請確保 ref struct 符合一次性模式。也就是説,它有一個實例 Dispose 方法,該方法是可訪問、無參數的並且具有 void 返回類型。可以將 using 語句或聲明與可釋放的 ref struct 的實例一起使用。
從 C# 13 開始,還可對 ref struct 類型上實現 IDisposable。但是,重載解析更傾向於 “可丟棄” 模式,而不是接口方法。僅當找不到合適的 Dispose 方法時,編譯器才會選擇調用 IDisposable . Dispose 方法。
實現接口的 ref struct 類型的限制
這些限制可確保實現接口的 ref struct 類型遵守必要的 ref 安全性規則。
- 無法將 ref struct 轉換為它實現的接口的實例。在參數為接口類型時,若你使用 ref struct 類型作為參數時,則此限制包括隱式轉換。該轉換會導致 boxed 轉換,這違反了 ref 安全性。ref struct 可以將方法聲明為顯式接口聲明。但是,這些方法只能通過類型參數為 allows ref struct 的泛型方法訪問。
- 實現某個接口的 ref struct 必須實現該接口的所有實例成員。即使該接口包含默認實現,ref struct 也必須實現其實例成員。
編譯程序強制實施這些限制。如果編寫實現接口的 ref struct 類型,則每個新更新都可能包含新的默認接口成員。在為任何新實例方法提供實現之前,應用程序不會編譯。不能為具有默認實現的 static 接口方法提供特定實現。
重要:實現接口的 ref struct 可能會導致後續的源代碼變更和二進制文件變更。如果 ref struct 實現在另一個程序集中定義的接口,並且該程序集提供將默認成員添加到該接口的更新,那麼就會發生這種變更。
重新編譯 ref struct 時會發生 “源斷裂”:即使存在默認實現,它也必須實現新成員。
如果升級外部程序集而不重新編譯 ref struct 類型,並且更新的代碼調用新方法的默認實現,則會發生二進制分離現象。訪問默認成員時,運行時引發異常。