痛点:写一个不可变数据类很啰嗦
public class PointOld
{
public int X { get; }
public int Y { get; }
public PointOld(int x, int y) { X = x; Y = y; }
public override bool Equals(object? obj) =>
obj is PointOld p && X == p.X && Y == p.Y;
public override int GetHashCode() => HashCode.Combine(X, Y);
public override string ToString() => $"PointOld {{ X = {X}, Y = {Y} }}";
}
15 行写一个二维点。
record class(C# 9+)
public record class Point(int X, int Y);
// 等价上面 15 行的所有
就这一行。编译器自动生成:
- 自动构造 + 自动 init-only 属性
Equals/GetHashCode(按值比较所有字段)ToString美化Deconstruct(解构)with表达式支持
var p1 = new Point(1, 2);
var p2 = new Point(1, 2);
Console.WriteLine(p1 == p2); // True(值相等)
Console.WriteLine(p1); // Point { X = 1, Y = 2 }
var (x, y) = p1; // 解构
var p3 = p1 with { X = 10 }; // 不改 p1,造新 record
Console.WriteLine(p3); // Point { X = 10, Y = 2 }
record vs class vs struct
public class Foo1 { } // 引用类型,引用相等,可变
public record Foo2(int X); // 引用类型,**值相等**,**不可变**
public struct Foo3 { } // 值类型,每次复制
public record struct Foo4(int X); // 值类型 + 值相等 + 默认不可变
record = record class(默认)。
何时用什么
| 用法 | 选 |
|---|---|
| 业务实体 / Service / 有方法和状态的类 | class |
| DTO / 配置 / 不可变数据载体 | record class(C# 现代默认) |
| 大量数据 + 性能敏感 | record struct 或 readonly struct |
| 小、不可变、明显是值(Point、Color、Money) | readonly struct 或 record struct |
| 需要继承 + 默认值比较 | record class(可继承) |
继承 record
public record class Animal(string Name);
public record class Dog(string Name, string Breed) : Animal(Name);
var d = new Dog("Rex", "Lab");
Console.WriteLine(d); // Dog { Name = Rex, Breed = Lab }
Animal a = d;
a == new Dog("Rex", "Lab"); // True(按 runtime 类型比,且包括所有字段)
record 支持继承(不像 struct)——一个 record class 可以从另一个 record class 继承。
自定义 record
public record class Person(string Name, int Age)
{
// 加方法 / 计算属性
public bool IsAdult => Age >= 18;
public string Greet() => $"Hi, I'm {Name}";
// 覆盖自动生成的 ToString
public override string ToString() => $"Person({Name}, {Age})";
}
主构造参数自动成为属性,类体内可以加其他成员。
readonly struct(C# 7.2+)
public readonly struct Vec2
{
public readonly double X;
public readonly double Y;
public Vec2(double x, double y) { X = x; Y = y; }
public Vec2 Add(in Vec2 other) => new(X + other.X, Y + other.Y);
}
readonly struct:所有字段必须 readonly、所有方法被视为不修改 this。编译器优化——传引用而不是复制。
record struct(C# 10+)
public readonly record struct Vec2(double X, double Y);
readonly record struct 把上面两个特性合一——性能 + 值语义 + 自动 == 三合一。新写的 Point / Vec / Money / Color 应该首选这种。
with 表达式
var p = new Point(1, 2);
var p2 = p with { X = 99 }; // 复制 + 改 X
with 等价于"复制构造 + 改字段"。深拷贝是浅层的(嵌套对象按引用复制)。
解构(Deconstruct)
var p = new Point(1, 2);
var (x, y) = p; // 自动生成的 Deconstruct
record 自动生成;class 要自己写:
public class Foo
{
public int A, B;
public void Deconstruct(out int a, out int b) { a = A; b = B; }
}
var (a, b) = new Foo { A = 1, B = 2 };
注意
- record 默认不可变——
with复制改字段,不就地改 - 想要可变 record:
public record class Mutable { public int X { get; set; } }(不推荐——失去 record 的核心价值) - record 字段如果是引用类型(如 List),
with的复制是浅的,要小心
现代 C# 实战推荐
// 配置 / 选项类
public record class ServerOptions(string Host, int Port, bool Ssl);
// API 模型
public record class UserDto(int Id, string Name, string Email);
// 算法用值类型
public readonly record struct Vec2(double X, double Y);
// Domain entity(有行为 + 标识)
public class User
{
public int Id { get; }
public string Email { get; private set; }
public void ChangeEmail(string newEmail) { ... }
}
→ 下一篇 泛型