C# 结构体(Struct)
值类型、栈上分配、什么时候用 struct 而不是 class。
Key Pointsstruct 是值类型,赋值复制整个内容;class 是引用类型,赋值复制引用。
定义与使用
C#
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public double Distance => Math.Sqrt(X * X + Y * Y);
public override string ToString() => $"({X},{Y})";
}
var p1 = new Point(3, 4);
var p2 = p1; // 复制整个结构体
p2.X = 100;
Console.WriteLine(p1);
Console.WriteLine(p2);
Console.WriteLine(p1.Distance);Output
(3,4)
(100,4)
5和 class 的差别
| Aspect | struct | class |
|---|---|---|
| 类型 | 值类型 | 引用类型 |
| 分配位置 | 栈 / 内联在容器里 | 堆 |
| 赋值 | 复制内容 | 复制引用 |
| 继承 | 不能继承 | 可以继承 |
| 默认构造 | 总是存在(全零) | 需自己定义 |
| 可为 null | 需 Point? | 本身可为 null |
| 适合 | 小、短命、值语义 | 有身份、需继承 |
什么时候用 struct
C#
// 适合:小、不可变、值语义明确
struct Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
public override string ToString() => $"{Amount} {Currency}";
}
var a = new Money(100, "CNY");
var b = a with { Amount = 200 }; // C# 10:非破坏性修改
Console.WriteLine(a);
Console.WriteLine(b);Output
100 CNY
200 CNY- 用 —— 字段少(≤16 字节理想)、逻辑上是值(坐标、金额、日期区间)
- 用 —— 需要大量创建且短命(性能敏感)
- 不用 —— 需要继承、需要引用语义、字段多或含引用类型
可变 struct 的坑
C#
struct Counter
{
public int Value;
public void Inc() => Value++;
}
var list = new List<Counter> { new Counter() };
// 编译错误:不能修改 foreach 变量
// foreach (var c in list) c.Inc();
// 这样改的是副本,没效果
var copy = list[0];
copy.Inc();
Console.WriteLine(list[0].Value);
// 正确:取出来改再放回
var item = list[0];
item.Inc();
list[0] = item;
Console.WriteLine(list[0].Value);Output
0
1Prefer immutable structs (
readonly fields or get-only properties). Mutable structs breed "I modified a copy" bugs inside collections, properties and foreach, and the compiler may not catch them.readonly struct 与 record struct
C#
// readonly struct:所有成员只读,避免防御性复制
readonly struct Temperature
{
public double Celsius { get; }
public Temperature(double c) => Celsius = c;
public double Fahrenheit => Celsius * 9 / 5 + 32;
}
// record struct(C# 10):自带值相等、ToString、with
record struct Size(int Width, int Height);
var t = new Temperature(100);
Console.WriteLine(t.Fahrenheit);
var s1 = new Size(3, 4);
var s2 = new Size(3, 4);
Console.WriteLine(s1 == s2);
Console.WriteLine(s1);Output
212
True
Size { Width = 3, Height = 4 }record struct auto-generates value semantics, value equality, printing and with — the modern way to write small value objects. The reference-type version is record class.
定义与使用
C#
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public double Distance => Math.Sqrt(X * X + Y * Y);
public override string ToString() => $"({X},{Y})";
}
var p1 = new Point(3, 4);
var p2 = p1; // 复制整个结构体
p2.X = 100;
Console.WriteLine(p1);
Console.WriteLine(p2);
Console.WriteLine(p1.Distance);Output
(3,4)
(100,4)
5和 class 的差别
| 对比项 | struct | class |
|---|---|---|
| 类型 | 值类型 | 引用类型 |
| 分配位置 | 栈 / 内联在容器里 | 堆 |
| 赋值 | 复制内容 | 复制引用 |
| 继承 | 不能继承 | 可以继承 |
| 默认构造 | 总是存在(全零) | 需自己定义 |
| 可为 null | 需 Point? | 本身可为 null |
| 适合 | 小、短命、值语义 | 有身份、需继承 |
什么时候用 struct
C#
// 适合:小、不可变、值语义明确
struct Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
public override string ToString() => $"{Amount} {Currency}";
}
var a = new Money(100, "CNY");
var b = a with { Amount = 200 }; // C# 10:非破坏性修改
Console.WriteLine(a);
Console.WriteLine(b);Output
100 CNY
200 CNY- 用 —— 字段少(≤16 字节理想)、逻辑上是值(坐标、金额、日期区间)
- 用 —— 需要大量创建且短命(性能敏感)
- 不用 —— 需要继承、需要引用语义、字段多或含引用类型
可变 struct 的坑
C#
struct Counter
{
public int Value;
public void Inc() => Value++;
}
var list = new List<Counter> { new Counter() };
// 编译错误:不能修改 foreach 变量
// foreach (var c in list) c.Inc();
// 这样改的是副本,没效果
var copy = list[0];
copy.Inc();
Console.WriteLine(list[0].Value);
// 正确:取出来改再放回
var item = list[0];
item.Inc();
list[0] = item;
Console.WriteLine(list[0].Value);Output
0
1struct 尽量做成不可变(所有字段
readonly 或只有 get 属性)。可变的 struct 在集合、属性、foreach 里到处都是「改了副本」的陷阱,而且编译器不一定报错。readonly struct 与 record struct
C#
// readonly struct:所有成员只读,避免防御性复制
readonly struct Temperature
{
public double Celsius { get; }
public Temperature(double c) => Celsius = c;
public double Fahrenheit => Celsius * 9 / 5 + 32;
}
// record struct(C# 10):自带值相等、ToString、with
record struct Size(int Width, int Height);
var t = new Temperature(100);
Console.WriteLine(t.Fahrenheit);
var s1 = new Size(3, 4);
var s2 = new Size(3, 4);
Console.WriteLine(s1 == s2);
Console.WriteLine(s1);Output
212
True
Size { Width = 3, Height = 4 }record struct 把「值语义 + 值相等 + 可打印 + with 复制」都自动生成,是写小值对象的现代方式。对应的引用类型版本是 record class。