ref 结构类型(C# 参考)

声明结构类型时使用ref修饰符。 在堆栈上分配类型的 ref struct 实例,它们无法转义到托管堆。 若要确保此属性,编译器会限制类型的使用 ref struct ,如下所示:

  • 不能将 a ref struct 用作数组的元素类型。
  • 不能将字段 ref struct 声明为类或非ref struct字段的类型。
  • 不能装箱 ref structSystem.ValueTypeSystem.Object
  • 无法在 lambda 表达式本地函数中捕获ref struct变量。
  • 在 C# 13 之前,不能在方法中使用ref structasync变量。 从 C# 13 开始,ref struct 变量不能在 await 方法中和 async 表达式用于相同的块。 但是,可以在同步方法中使用 ref struct 变量,例如,在返回 TaskTask<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 时)。

C# 语言参考记录了最近发布的 C# 语言版本。 它还包含即将发布的语言版本公共预览版中功能的初始文档。

本文档标识了在语言的最后三个版本或当前公共预览版中首次引入的任何功能。

小窍门

若要查找 C# 中首次引入功能时,请参阅 有关 C# 语言版本历史记录的文章。

通常,如果需要一种同时包含 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 字段

可以在 /> 中声明字段,如以下示例所示:

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:只能使用= ref构造函数或init访问器内的运算符重新分配此字段。 可以在字段访问修饰符允许的任何时间点使用 = 运算符分配值。
  • ref readonly:在任何时候,你都无法向此字段分配具有 = 运算符的值。 但是,可以使用运算符重新分配字段 = ref
  • readonly ref readonly:只能在构造函数或 init 访问器中重新分配此字段。 在任何时候,都不能为字段赋值。

编译器确保存储在 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 开始,还可对 IDisposable 类型上实现 ref struct。 但是,重载解析更倾向于可释放模式,而不是接口方法。 仅当找不到合适的IDisposable.Dispose方法时,编译器才会解析为Dispose方法。

实现接口的 ref struct 类型的限制

这些限制可确保 ref struct 实现接口的类型遵循必要的 ref 安全 规则。

  • 无法将它 ref struct 实现的接口的实例转换为它实现的接口实例。 此限制包括在将 ref struct 类型用作参数时隐式转换,参数是接口类型。 该转换会导致装箱转换,这违反了 ref 安全性。 ref struct 可以将方法声明为显式接口声明。 但是,只能从类型参数 allows ref struct 类型的泛型方法访问这些方法。
  • 实现接口 ref struct 必须 实现所有实例接口成员。 即使接口包含默认实现,ref struct 也必须实现实例成员。

编译程序强制实施这些限制。 如果编写实现接口的 ref struct 类型,则每个新更新都可能包含新的默认接口成员。 在为任何新实例方法提供实现之前,应用程序不会编译。 不能为具有默认实现的 static 接口方法提供特定实现。

重要

使用类型实现接口 ref struct 会导致以后发生源中断和二进制中断性变更。 如果 ref struct 实现在另一个程序集中定义的接口,并且该程序集提供向该接口添加默认成员的更新,则会发生中断。

重新编译 ref struct源中断时会发生:即使存在默认实现,它也必须实现新成员。

如果在不重新编译 ref struct 类型的情况下升级外部程序集, 并且 更新的代码将调用新方法的默认实现,则会发生二进制中断。 访问默认成员时,运行时引发异常。

C# 语言规范

有关更多信息,请参阅 C# 语言规范的以下部分:

有关 ref 字段的详细信息,请参阅低级别结构改进建议说明。

另请参阅