ref
结构类型(C# 参考)
你可以在结构类型的声明中使用 ref
修饰符。 ref struct
类型的实例是在堆栈上分配的,不能转义到托管堆。 为了确保这一点,编译器将 ref struct
类型的使用限制如下:
ref struct
不能是数组的元素类型。ref struct
不能是类或非ref struct
的字段的声明类型。ref struct
不能被装箱为 System.ValueType 或 System.Object。ref struct
变量不能在 Lambda 表达式或本地函数中捕获。- 在 C# 13 之前,
ref struct
无法在方法中使用async
变量。 从 C# 13 开始,ref struct
变量不能在async
方法中和await
表达式用于相同的块。 但是,可以在同步方法中使用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
可以是类型参数(当该类型参数在其where
子句中指定allows ref struct
时)。
通常,如果需要一种同时包含 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 struct
中声明 ref
字段,如以下示例所示:
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;
}
}
ref
字段可以具有 null
值。 使用 Unsafe.IsNullRef<T>(T) 方法确定 ref
字段是否为 null
。
可通过以下方式将 readonly
修饰符应用于 ref
字段:
readonly ref
:只能在构造函数或init
访问器中使用= 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
类型作为参数时,则此限制包括隐式转换。 该转换会导致装箱转换,这违反了 ref 安全性。 - 实现接口的
ref struct
必须实现所有接口成员。ref struct
必须实现接口包含默认实现的成员。
编译程序强制实施这些限制。 如果编写实现接口的 ref struct
类型,则每个新更新都可能包含新的默认接口成员。 在为这些新方法提供实现之前,应用程序不会编译。
重要
实现接口的 ref struct
包括以后进行源中断性和二进制中断性变更的可能性。 如果 ref struct
实现在另一个程序集中定义的接口,并且该程序集提供将默认成员添加到该接口的更新,则会发生中断。
重新编译 ref struct
时发生源中断:即使存在默认实现,它也必须实现新成员。
如果升级外部程序集而不重新编译 ref struct
类型,并且更新的代码调用新方法的默认实现,则会发生二进制中断。 访问默认成员时,运行时引发异常。
C# 语言规范
有关更多信息,请参阅 C# 语言规范的以下部分:
有关 ref
字段的详细信息,请参阅低级别结构改进建议说明。