痛点:写一个不可变数据类很啰嗦

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 structreadonly struct
小、不可变、明显是值(Point、Color、Money) readonly structrecord 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) { ... }
}

→ 下一篇 泛型