.NET 结构体

我们都知道结构体struct,在大量创建、引用的场景下相比类class能提升很大性能。本文介绍下如何正确使用struct

struct定义

复制代码
 1 struct Point
 2 {
 3     public float X;
 4     public float Y;
 5 }
 6 
 7 class Rect
 8 {
 9     public Point Position;
10 }
11 
12 Rect rect = new Rect();
复制代码

上述代码中,rect的Position字段并没有分配到栈上,反而是和Rect的实例一起被分配到了托管堆中。除此之外,装箱也会导致结构体实例分配到托管堆:

1 Point point = new Point();
2 object obj = point; // 装箱,结构体转移到托管堆

所以如果因为性能要求或者其它原因想限制struct只分配到栈上的话,可以添加ref:

1 ref struct Point
2 {
3     public float X;
4     public float Y;
5 }

这种情况下,struct关键字前添加ref是指将结构体声明为只能在栈上分配的结构体,对于这种结构体,任何可能将其转移到托管堆的行为都将被阻止(例如在引用类型中定义ref结构体字段,或者进行装箱操作):

1 class Rect
2 {
3     public Point Position; // 错误,Position会随着Rect实例转移到托管堆
4 }
5 
6 Point point = new Point();
7 object obj = point; // 错误,point会被装箱到托管堆

另外,ref声明的struct可以继续作为字段在新struct内声明:

1 ref struct PointNew
2 {
3     public Point Point; // 允许,因为XPoint同样保证了栈上分配
4 }

还可以声明只读的ref结构体:

1 readonly ref struct PointNew
2 {
3     public Point Point; // 允许,因为XPoint同样保证了栈上分配
4 }

使用struct

1. 结构体作为参数传递

1 Point p = new Point();
2 TestAdd(ref p);
3 
4 void TestAdd(ref Point point)
5 {
6     point.X += 1;
7 }

如果不是只传值而是需要变更数据,需要添加ref,否则TestAdd中point只会作为原来p的副本生成一个新结构体。那最终p中的X值还是初始值0。ref我们都熟悉了,值类型如果作为参数输入后,外部同步修改也是使用ref

2. 结构体存储大小

看下面这段代码,输出结构体在栈上所需的内存大小:

复制代码
 1     [StructLayout(LayoutKind.Auto)]
 2     struct TestStorage
 3     {
 4         public byte A;
 5         public int B;
 6         public byte C;
 7         public byte D;
 8         public byte E;
 9     }
10    // 输出
11     unsafe
12     {
13         Debug.WriteLine(sizeof(TestStorage));
14     }
复制代码

上面输出的值是12。我们聊下内存布局:

1)Sequential:顺序布局,按字段的声明顺序布局,是默认行为

2)Auto:自动布局,自动排列字段顺序以用最小的空间来储存字段

3)Explicit:显式布局,手动指定字段地址的偏移

一般来说,byte是1,int是4。

在不添加[StructLayout(LayoutKind.Auto)]标记时,由于需要内存对齐,字段B是4,那内存大小就需要以4+4+4=12来分配,前面4存储字段A 1的内存,后面4存储字段CDE一共3的内存大小

添加[StructLayout(LayoutKind.Auto)]后,内存对齐,只需要4+4=8的内存大小

 

我们使用Struct场景主要有以下使用场景:

  • 一般是某类数据使用较多、有较大的内存问题 -  对于不需要频繁分配和释放的大量小对象,可以使用结构体减少堆内存的压力
  • 现有类对象创建回收过于频繁,性能有较大损耗 - 可以使用结构体,减少对象创建、垃圾回收的开销

结构体适合用于封装简单的数据,例如坐标点(Point)、复数(Complex Number)、时间段(TimeSpan)等

参考文章:

结构类型 - C# reference | Microsoft Learn

Struct 和 Class 的区别以及使用场景_struct和class使用场景-CSDN博客

posted @   唐宋元明清2188  阅读(121)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
点击右上角即可分享
微信分享提示