.NET 新特性(四):.NET 8(C# 12)成熟与 NativeAOT

写在前面

本文是 .NET 新特性系列第四篇。.NET 8(2023-11)是当前最成熟的 LTS。三大亮点:主构造函数(C# 终于有)、集合表达式(统一初始化)、NativeAOT 正式发布(编译成原生代码,启动极快)。

加上 TimeProvider(可测试的时间)、Frozen 集合(极快只读)等,.NET 8 是目前生产首选。


一、版本概览

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.NET 8
  发布:2023-11
  支持:LTS(3 年)
  C#:12
  定位:成熟、生产首选 LTS

  亮点:
    ✓ NativeAOT 正式发布
    ✓ 主构造函数、集合表达式
    ✓ TimeProvider(可测试的时间)
    ✓ Frozen 集合、性能大幅提升

二、C# 12 语言特性(重点)

2.1 主构造函数(primary constructors)⭐

类/结构体的构造参数直接成为类成员(C# 终于有,之前 VB / Record 有):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 旧:字段 + 构造函数赋值
public class UserService
{
    private readonly IUserRepo _repo;
    private readonly ILogger<UserService> _logger;
    public UserService(IUserRepo repo, ILogger<UserService> logger)
    {
        _repo = repo; _logger = logger;
    }
}

// 新(C# 12):主构造函数,参数直接用
public class UserService(IUserRepo repo, ILogger<UserService> logger)
{
    public User Get(int id) => repo.Get(id);   // 直接用 repo
}

// 配合 init-only / 全局可访问
public class Point(double x, double y)
{
    public double X => x;     // 暴露为属性
    public double Y => y;
    public Point() : this(0, 0) { }   // 链式调用其他构造
}
1
2
3
主构造函数大幅减少样板代码
  DI 注入、值传递场景特别爽
  注意:参数不是字段,要在用它的方法里捕获(或包成属性)

2.2 集合表达式(collection expressions)⭐

统一所有集合的初始化语法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 以前:不同集合不同初始化
int[] a = new[] { 1, 2, 3 };
List<int> b = new() { 1, 2, 3 };
IEnumerable<int> c = new List<int> { 1, 2, 3 };
Span<int> d = stackalloc int[] { 1, 2, 3 };
ImmutableArray<int> e = ImmutableArray.Create(1, 2, 3);

// 现在:统一用 [ ... ]
int[] a = [1, 2, 3];
List<int> b = [1, 2, 3];
IEnumerable<int> c = [1, 2, 3];
Span<int> d = [1, 2, 3];
ImmutableArray<int> e = [1, 2, 3];

// 展开(..):合并集合
int[] all = [0, .. existingArray, .. otherList, 4];
1
2
3
4
5
集合表达式编译器按目标类型选择最优实现:
  Span → stackalloc(栈分配,零 GC)
  ImmutableArray → 直接构造
  List → List
  统一、简洁、高性能

2.3 using 别名任意类型

1
2
3
4
5
6
7
8
// 别名不只是命名空间/类,可以是元组、泛型
using Point = (int X, int Y);
using Dict = System.Collections.Generic.Dictionary<string, int>;

Point p = (1, 2);
Dict d = new();

// 元组别名让"轻量结构"更易用

2.4 ref readonly 参数

1
2
3
4
// in:按引用传,只读,防拷贝
// .NET 8:ref readonly 参数
void Process(ref readonly BigStruct data) { }
// 明确表达:引用传 + 只读(in 的语义,但更显式)

2.5 其他

1
2
3
4
5
6
// inline arrays([InlineArray(N)] 固定大小数组,安全 + 快)
// lambda 默认参数
Func<int, int> f = (int x = 1) => x * 2;

// Interceptor(实验性,源生成器拦截方法调用,热重载基础)
// ref struct 放宽

三、运行时与性能

3.1 NativeAOT 正式发布 ⭐

1
2
3
4
# 编译成原生代码,无需 .NET 运行时
dotnet publish -c Release -r linux-x64 -p:PublishAot=true

# 输出单个原生可执行文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
NativeAOT 的意义:
  ✓ 启动极快(毫秒级,无 JIT)
  ✓ 内存占用小(无运行时)
  ✓ 单文件部署,无依赖
  ✓ 适合云函数、CLI、边缘、容器
  
  限制:
    ✗ 反射受限(需 trim / 显式标注)
    ✗ 动态加载程序集不支持
    ✗ 部分库不兼容

  ASP.NET Core 也部分支持 AOT(Minimal API)

3.2 Server GC 动态适应

1
2
3
4
.NET 8 Server GC 根据工作负载动态调整堆大小
  高负载 → 分配更多堆
  低负载 → 释放内存给系统
  容器环境特别友好(内存占用更可控)

3.3 性能改进

1
2
3
4
5
✓ JIT 改进(更多向量化、内联)
✓ async state machine 更小
✓ 字符串操作、Span 改进
✓ System.Text.Json 性能(已是 .NET 最快 JSON)
✓ AOT、PGO 持续优化

四、BCL 改进(重头)

4.1 TimeProvider(可测试的时间)⭐

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 以前:DateTime.Now 难测试(依赖真实时间)
// .NET 8:TimeProvider 抽象时间

public class OrderService(TimeProvider time)
{
    public bool IsExpired(Order o)
        => time.GetUtcNow() > o.ExpireTime;
}

// 生产用真实时间
new OrderService(TimeProvider.System);

// 测试用虚拟时间
var fake = new FakeTimeProvider();
fake.SetUtcNow(new DateTimeOffset(2026, 1, 1));
new OrderService(fake);   // 可控测试

4.2 Frozen 集合 ⭐

1
2
3
4
5
6
7
// 构建后不可变,但读取极快(编译期优化)
var frozen = FrozenDictionary.CreateRange(data);
var set = FrozenSet.CreateRange(items);

frozen.TryGetValue(key, out var v);   // 比普通 Dictionary 读还快

// 适用:启动时构建、之后只读的查找表(配置、枚举映射)

4.3 SearchValues

1
2
3
// 快速字符集合查找(向量化)
var digits = SearchValues.Create("0123456789");
int idx = "abc123".AsSpan().IndexOfAny(digits);   // 3

4.4 System.Text.Json 多态

1
2
3
4
5
[JsonPolymorphic]
[JsonDerivedType(typeof(Dog), "dog")]
[JsonDerivedType(typeof(Cat), "cat")]
public abstract class Animal { }
// 序列化派生类带类型标识,反序列化还原正确类型

4.5 其他

1
2
3
4
5
Random.Shared.GetItems(arr, 5);   // 随机取 N 个
Random.Shared.Shuffle(arr);        // 洗牌
// KeyedDi(Keyed 服务,按 key 注入)
builder.Services.AddKeyedTransient<IRepo, SqlRepo>("sql");
// ConfigureContainer、Numerics 改进

五、ASP.NET Core 8

1
2
3
4
5
6
✓ Blazor United(Server + WebAssembly 混合渲染)
✓ Server GC 默认开启(模板默认)
✓ AOT 支持(Minimal API 可 AOT)
✓ 缓存、限流(来自 7)完善
✓ 身份认证改进、OpenAPI
✓ .NET Aspire(云原生开发框架,preview)

六、升级建议

1
2
3
4
5
6
7
8
.NET 8 是当前最稳的 LTS,强烈推荐生产使用

新项目直接 8(或更新的 LTS 10)
升级收益:
  ✓ NativeAOT(启动/内存)
  ✓ 主构造函数、集合表达式(生产力)
  ✓ TimeProvider(可测试)
  ✓ Frozen 集合、性能

七、小结

.NET 8 是成熟的 LTS:

  • C# 12:主构造函数、集合表达式、using 别名任意类型、ref readonly
  • 运行时:NativeAOT 正式、Server GC 动态适应、性能大幅提升
  • BCL:TimeProvider(可测试时间)、Frozen 集合、SearchValues、JSON 多态、Keyed DI
  • ASP.NET Core:Blazor United、Server GC 默认、AOT
  • 定位:生产首选 LTS

下一篇讲 .NET 9(C# 13):params 集合、Lock 类型、HybridCache。