导读:本期聚焦于小伙伴创作的《C#中ref struct是什么?如何创建只能在栈上分配的结构体?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#中ref struct是什么?如何创建只能在栈上分配的结构体?》有用,将其分享出去将是对创作者最好的鼓励。

C#的ref struct是一种特殊的结构体类型,它的核心特性是只能被分配到栈上,无法被分配到堆内存中,这一设计主要是为了优化高频使用的临时对象的内存分配,减少垃圾回收的负担。ref struct的出现让开发者可以更精细地控制值类型的内存分配位置,在需要高性能的场景下非常实用。

C#中ref struct是什么?如何创建只能在栈上分配的结构体?

ref struct的定义方式

定义ref struct的语法非常简单,只需要在struct关键字前加上ref修饰符即可,和普通结构体的定义方式差异很小。下面是一个基础的ref struct定义示例:

// 定义一个只能在栈上分配的ref struct
public ref struct StackOnlyStruct
{
    // 结构体字段
    public int Id;
    public string Name;

    // 结构体方法
    public void PrintInfo()
    {
        Console.WriteLine($"Id: {Id}, Name: {Name}");
    }
}

上述代码中,StackOnlyStruct就是一个标准的ref struct,它不能被分配到堆上,只能存在于栈内存中。

ref struct的使用规则

ref struct由于分配位置的限制,有一系列必须遵守的使用规则,违反这些规则会导致编译错误。

1. 不能被装箱到堆上

ref struct不允许进行装箱操作,因为装箱会把值类型放到堆上的对象中,这违反了ref struct只能栈分配的规则。下面的代码会编译失败:

public ref struct TestRefStruct
{
    public int Value;
}

class Program
{
    static void Main()
    {
        TestRefStruct s = new TestRefStruct();
        // 错误:ref struct不能被装箱
        object obj = s;
        // 错误:也不能转换为object类型
        Console.WriteLine(s);
    }
}

2. 不能作为类的字段或属性

类的实例是分配在堆上的,如果ref struct作为类的字段,就意味着ref struct会被放到堆中,因此这是不被允许的。下面的定义会编译错误:

public ref struct RefStructA
{
    public int X;
}

// 错误:类不能包含ref struct类型的字段
public class NormalClass
{
    // 编译错误,RefStructA是ref struct,不能作为类的字段
    public RefStructA Field;
}

但是如果结构体本身也是ref struct,那么它可以包含另一个ref struct作为字段,因为两个都只能在栈上:

public ref struct InnerRefStruct
{
    public int A;
}

public ref struct OuterRefStruct
{
    // 合法:ref struct可以包含另一个ref struct作为字段
    public InnerRefStruct Inner;
    public int B;
}

3. 不能实现接口

当ref struct实现接口时,它可能会被隐式装箱为接口类型(接口是引用类型,存在于堆上),因此ref struct不允许实现任何接口,下面的代码会编译失败:

public interface ITest
{
    void Do();
}

// 错误:ref struct不能实现接口
public ref struct RefStructB : ITest
{
    public void Do()
    {
        Console.WriteLine("Do something");
    }
}

4. 不能作为泛型类型参数

泛型类型参数可能会被实例化到堆上的类型中,因此ref struct不能作为泛型方法的类型参数或者泛型类的类型参数,下面的使用方式会编译错误:

public ref struct RefStructC
{
    public int Val;
}

class Program
{
    // 错误:ref struct不能作为泛型类型参数
    static void GenericMethod<T>() where T : RefStructC { }

    static void Main()
    {
        // 错误:也不能传递ref struct作为泛型参数
        GenericMethod<RefStructC>();
    }
}

5. 可以在栈上作为局部变量或方法参数

ref struct最常见的使用场景就是作为方法的局部变量或者方法的参数,这两种场景下的分配都在栈上,符合ref struct的规则:

public ref struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public void Move(int dx, int dy)
    {
        X += dx;
        Y += dy;
    }
}

class Program
{
    // ref struct可以作为方法参数
    static void PrintPoint(Point p)
    {
        Console.WriteLine($"X: {p.X}, Y: {p.Y}");
    }

    static void Main()
    {
        // ref struct作为局部变量,分配在栈上
        Point p = new Point(10, 20);
        p.Move(5, 5);
        PrintPoint(p);
    }
}

ref struct的适用场景

ref struct最适合用在高频率创建、生命周期短的临时对象场景,比如:

  • 高性能的数值计算类型,比如向量、矩阵等临时计算对象,避免频繁分配堆内存触发GC。
  • Span<T>和ReadOnlySpan<T>本身就是ref struct类型,用于安全高效地访问连续内存区域,不需要额外的堆分配。
  • 临时数据缓冲区,比如方法内部使用的临时数组包装类型,只在方法执行期间存在,不需要长期存储。

ref struct的注意事项

虽然ref struct能优化内存分配,但使用时需要注意不要超出它的生命周期范围。比如不能把ref struct的引用存储到堆上的对象中,也不能从方法返回ref struct的引用给堆上的变量。如果需要在多个方法间传递ref struct,可以使用ref参数传递引用,确保它始终在栈上:

public ref struct TempBuffer
{
    public byte[] Data;
    public int Length;
}

class Program
{
    // 使用ref参数传递ref struct,避免拷贝和堆分配
    static void ProcessBuffer(ref TempBuffer buffer)
    {
        for (int i = 0; i < buffer.Length; i++)
        {
            buffer.Data[i] = (byte)(buffer.Data[i] + 1);
        }
    }

    static void Main()
    {
        byte[] arr = new byte[10];
        TempBuffer buffer = new TempBuffer { Data = arr, Length = 10 };
        ProcessBuffer(ref buffer);
    }
}

只要严格遵循ref struct的使用规则,就可以安全地用它来优化程序的内存性能,减少不必要的垃圾回收开销。

ref_structC#栈分配结构体修改时间:2026-06-28 01:57:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。