结构体是一种值类型,用于封装一组相关的数据,使其成为一个单一的变量单元。它和类(Class)超级类似,但存在一些关键性的区别,这些区别决定了它们的使用场景。
1. 核心概念:值类型 vs. 引用类型
这是理解结构体最根本的一点。
- 结构体是值类型:当一个结构体变量被赋值给另一个变量,或者作为参数传递给方法时,发生的是内容的完整拷贝。修改拷贝后的数据不会影响原始数据。
Point p1 = new Point(10, 20);
Point p2 = p1; // 这里进行的是值拷贝,p2 是 p1 的一个独立副本
p2.X = 100; // 只修改 p2
Console.WriteLine(p1.X); // 输出: 10 (p1 完全没有被影响)
Console.WriteLine(p2.X); // 输出: 100
类是引用类型:当类对象被赋值或传递时,发生的是引用的拷贝(类似于拷贝一个快捷方式)。两个变量指向内存中的同一个对象。修改其中一个,另一个也会看到变化。
MyClass obj1 = new MyClass { Data = 10 };
MyClass obj2 = obj1; // 这里拷贝的是引用,obj2 和 obj1 指向同一个对象
obj2.Data = 100; // 通过 obj2 修改对象
Console.WriteLine(obj1.Data); // 输出: 100 (obj1 也看到了修改)
Console.WriteLine(obj2.Data); // 输出: 100
2. 如何定义结构体
使用 struct 关键字定义。其成员定义方式与类几乎完全一致(字段、属性、方法、构造函数等)。
// 定义一个表明点的结构体
public struct Point
{
// 字段
public int X;
public int Y;
// 带参数的构造函数(必须初始化所有字段)
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
// 方法
public void Print()
{
Console.WriteLine($"({X}, {Y})");
}
// 属性
public double DistanceFromOrigin => Math.Sqrt(X * X + Y * Y);
}
结构体的特点与限制
- 隐式继承:所有结构体都隐式继承自 System.ValueType,而 ValueType 又继承自 object。这意味着结构体拥有 ToString()、GetHashCode() 等方法,但不能显式继承其他类或结构体(不支持继承)。
- 构造函数:
- 你可以定义带参数的构造函数,但必须初始化所有实例字段。
- 编译器会自动生成一个无参构造函数(new Point()),你不能自己定义一个无参构造函数。这个无参构造函数会将所有字段设置为其默认值(0、false、null 等)。
- 字段初始化:不能在结构体声明时直接初始化实例字段。
public struct BadExample
{
// public int Value = 10; // 编译错误!不允许这样初始化
public int Value; // 正确,必须在构造函数中初始化
}
- 性能思考:由于是值类型,结构体一般分配在栈(Stack) 上(但这并非绝对,例如作为类的成员时,它会随类实例分配在堆上)。对于小型、生命周期短的数据,这可以减少垃圾回收(GC)的压力,性能更高。但对于大型结构体,频繁的拷贝操作可能会带来性能损失。
4. 何时使用结构体(Guidelines)
根据微软的官方提议,在以下情况下思考使用结构体:
- 逻辑上表明一个简单的值,类似于原始类型(int, double)。
- 实例大小应小于 16 字节。如果太大,拷贝成本会很高,反而降低性能。
- 它是不可变的(Immutable)。这是一个超级重大的实践。创建后,其状态不应再改变。这样能避免值拷贝语义带来的困惑。
- 它不会频繁地被装箱(Boxing)。(装箱是将值类型转换为 object 引用类型的过程,有性能开销)。
- 不需要继承自其他类型或被其他类型继承。
典型应用场景:
- 坐标点(Point)
- 复数(Complex)
- 颜色(Color)(RGBA值)
- 轻量级的键值对(KeyValuePair)
- 其他简单的数据容器
5. 结构体 vs. 类 (总结)
|
特性 |
结构体 (Struct) |
类 (Class) |
|
类型 |
值类型 |
引用类型 |
|
分配位置 |
一般在线程栈上 |
托管堆上 |
|
赋值操作 |
拷贝内容 |
拷贝引用 |
|
继承 |
不能显式继承,隐式继承自 ValueType |
支持继承和多态 |
|
构造函数 |
不能自定义无参构造函数 |
可以自定义无参构造函数 |
|
字段初始化 |
声明时不能初始化实例字段 |
声明时可以初始化实例字段 |
|
默认值 |
不能为 null(可空类型 Nullable 除外) |
可以为 null |
|
适用场景 |
小型、简单、不可变的数据 |
复杂的对象、需要继承、需要共享引用 |
总结
C# 中的结构体是一种轻量级的值类型,适用于封装小型、简单的数据集合,尤其是那些不可变的数据。它的值语义(拷贝行为)是其与类的核心区别。正确使用结构体可以提高应用程序的性能,但错误地使用(例如创建过大的可变结构体)则会适得其反。在大多数表明复杂逻辑或状态的场景中,类依旧是首选。
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END
















- 最新
- 最热
只看作者