深入理解 C# 结构体:定义、用法与与类的核心差异
C# 结构体深度解析
全面掌握 C# 结构体:定义、实例化、构造函数、属性,以及与类的本质区别(值类型 vs 引用类型),附实战代码示例。
在 C# 中,结构体 与类语法相似,但本质不同:结构体是值类型,数据直接存储在栈上(或内联在包含对象中),而非堆上引用。这带来了性能与语义上的显著差异。
假设需存储人员姓名与年龄。若用单独变量 name、age,多人时极其繁琐。定义一个包含 name 与 age 的结构体,即可为每个人复用相同布局。
定义结构体
使用 struct 关键字:
struct Employee {
public int Id;
}
Id 是结构体字段。结构体还可包含方法、索引器、属性、事件、运算符重载等。
声明结构体变量
使用前必须创建变量:
struct Employee {
public int Id;
}
// 声明 Employee 类型变量 emp
Employee emp;
访问结构体成员
使用点运算符 .:
Employee emp;
emp.Id = 1;
int、bool、float 等)本质上都是预定义结构体。完整示例:基础结构体用法
using System;
namespace CsharpStruct {
struct Employee {
public int Id;
public void GetId(int id) {
Console.WriteLine("员工 ID: " + id);
}
}
class Program {
static void Main(string[] args) {
Employee emp;
emp.Id = 1;
emp.GetId(emp.Id);
Console.ReadLine();
}
}
}
输出
员工 ID: 1
也可用 new 实例化(调用无参构造函数,字段初始为默认值):
Employee emp = new Employee();
结构体构造函数
结构体可定义构造函数初始化字段:
struct Employee {
public int Id;
public string Name;
// 参数化构造函数
public Employee(int id, string name) {
Id = id;
Name = name;
}
}
示例:带构造函数的结构体
using System;
namespace CsharpStruct {
struct Employee {
public int Id;
public string Name;
public Employee(int id, string name) {
Id = id;
Name = name;
}
}
class Program {
static void Main(string[] args) {
Employee emp = new Employee(1, "Brian");
Console.WriteLine("员工姓名: " + emp.Name);
Console.WriteLine("员工 ID: " + emp.Id);
Console.ReadLine();
}
}
}
输出
员工姓名: Brian 员工 ID: 1
构造函数必须为所有字段赋值,否则编译报错。
结构体属性
结构体支持属性封装字段:
using System;
namespace CsharpStruct {
struct Employee {
private int id;
public int Id {
get { return id; }
set { id = value; }
}
}
class Program {
static void Main(string[] args) {
Employee emp = new Employee();
emp.Id = 1;
Console.WriteLine("员工 ID: " + emp.Id);
Console.ReadLine();
}
}
}
输出
员工 ID: 1
结构体 vs 类:核心差异对决
尽管语法相似,但本质不同:
| 特性 | 类 | 结构体 |
|---|---|---|
| 类型分类 | 引用类型 | 值类型 |
| 赋值行为 | 复制引用(指向同一对象) | 复制整个值(独立副本) |
| 继承支持 | 支持继承(单继承) | 不支持继承(隐式继承 ValueType,不可再继承) |
| 默认值 | null | 所有字段按类型默认值(0、false、null 等) |
| 内存分配 | 堆 | 栈(或内联在包含对象中) |
| 垃圾回收 | 受 GC 管理 | 无 GC 压力(栈上自动释放) |
| 可为 null | 天然可为 null | 需用 Nullable<T> 或 T? |
引用类型行为演示(类)
using System;
namespace CsharpStruct {
class Employee {
public string Name;
}
class Program {
static void Main(string[] args) {
Employee emp1 = new Employee();
emp1.Name = "John";
Employee emp2 = emp1; // 复制引用
emp2.Name = "Ed";
Console.WriteLine("Employee1 姓名: " + emp1.Name);
Console.ReadLine();
}
}
}
输出
Employee1 姓名: Ed
emp2 与 emp1 指向同一对象,修改 emp2 影响 emp1。
值类型行为演示(结构体)
using System;
namespace CsharpStruct {
struct Employee {
public string Name;
}
class Program {
static void Main(string[] args) {
Employee emp1 = new Employee();
emp1.Name = "John";
Employee emp2 = emp1; // 复制整个值
emp2.Name = "Ed";
Console.WriteLine("Employee1 姓名: " + emp1.Name);
Console.ReadLine();
}
}
}
输出
Employee1 姓名: John
emp2 获得 emp1 的独立副本,修改互不影响。
何时选择结构体?
- ✅ 小型、不可变 数据结构(如坐标点、日期范围、RGB 颜色)
- ✅ 需要值语义(赋值即复制)的场景
- ✅ 高频创建/销毁、对 GC 压力敏感的热路径
- ✅ 互操作场景(P/Invoke、GPU 缓冲区)需精确内存布局
- ❌ 大型对象、频繁作为参数传递(复制开销大)
- ❌ 需要多态/继承的场景
- ❌ 需要
null语义且不想用Nullable<T>的场景
现代 C# 结构体增强(C# 7.2+)
readonly struct:编译器强制所有成员只读,保证不可变性ref struct:仅能栈分配,不能装箱、不能作为类字段、不能用于 async/iterator,适合Span<T>等高性能场景in参数修饰符:以只读引用传递大型结构体,避免复制开销ref readonly返回:返回结构体引用但禁止修改
这些特性让结构体在高性能库开发中大放异彩。
C语言