C# 结构体(Struct)

值类型、栈上分配、什么时候用 struct 而不是 class。

Intermediate
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 的差别

Aspectstructclass
类型值类型引用类型
分配位置栈 / 内联在容器里堆
赋值复制内容复制引用
继承不能继承可以继承
默认构造总是存在(全零)需自己定义
可为 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
1
Prefer 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.

Friends & Partners