版本定位

适用版本:.NET 8 前置知识:.NET 7 基础、NuGet 包管理

背景

.NET 8 不只是 C# 12 的更新,运行时和 SDK 也带来了大量改进。从 TimeProvider 抽象时间 API动态 PGO 优化,从 SearchValues 高性能搜索验证属性增强,这些改进直接影响你的开发效率和应用性能。

新特性一览

特性 简述 实用性
Native AOT 改进 启动快 2-3 倍,内存减 30-50% ⭐⭐⭐⭐⭐
动态 PGO 运行时自动优化热点代码 ⭐⭐⭐⭐⭐
TimeProvider 抽象时间 统一时间抽象,可 mock ⭐⭐⭐⭐⭐
SearchValues 高性能字符/字节搜索 ⭐⭐⭐⭐⭐
FrozenDictionary/FrozenSet 优化后的不可变集合 ⭐⭐⭐⭐⭐
SHA-3 哈希算法 最新加密标准 ⭐⭐⭐⭐
System.Text.Json 改进 JsonSchema、枚举转换 ⭐⭐⭐⭐⭐
验证属性改进 更多内置验证器 ⭐⭐⭐⭐
ZipFile 流式压缩 大文件不再 OOM ⭐⭐⭐⭐
COM 互操作源生成器 AOT 兼容的 COM 调用 ⭐⭐⭐⭐
优先队列改进 PriorityQueue 增强 ⭐⭐⭐⭐
Vector512 硬件加速 512 位 SIMD 支持 ⭐⭐⭐
DateOnly/TimeOnly 改进 更多解析方法 ⭐⭐⭐⭐
MetadataLoadContext 安全的反射分析 ⭐⭐⭐
委托缓存 内存优化 ⭐⭐⭐
GC 并发压缩 更低延迟 ⭐⭐⭐⭐
JIT 编译优化 内联、循环优化 ⭐⭐⭐⭐
HybridGlobalization ICU 混合全球化 ⭐⭐⭐
NuGet 安全改进 漏洞检测、签名验证 ⭐⭐⭐⭐
测试框架改进 Code Coverage、测试过滤器 ⭐⭐⭐⭐
包体积缩减 Package Trimming 增强 ⭐⭐⭐⭐
模板引擎改进 更好的项目模板 ⭐⭐⭐
容器化改进 控制台应用支持 Docker ⭐⭐⭐⭐

特性详解

1. Native AOT 改进

Native AOT 在 .NET 7 中引入,.NET 8 进行了大幅改进:

<!-- 启用 Native AOT -->
<PropertyGroup>
    <PublishAot>true</PublishAot>
</PropertyGroup>
对比项 JIT 模式 Native AOT
启动时间 基准 快 2-3 倍
内存占用 基准 减少 30-50%
部署包 包含运行时 只包含必要代码
兼容性 完整 部分 API 受限

改进点

  • 启动时间:比 JIT 模式快 2-3 倍

  • 内存占用:减少 30-50%

  • 部署包大小:只包含必要的代码

  • 兼容性:支持更多 API(序列化、反射、HTTP 客户端等)

适用场景:命令行工具、微服务、无服务器函数、容器化应用。

2. 动态 PGO(Profile-Guided Optimization)

动态 PGO 是 .NET 8 最重要的性能特性之一,运行时自动收集执行信息并优化热点代码。

// .NET 7 及之前:需要手动收集 profile 并重新编译
// .NET 8:运行时自动完成,无需任何配置

// 启用方式(默认已启用)
// <TieredPGO>true</TieredPGO>  <!-- .NET 8 默认开启 -->

// 查看 PGO 效果
dotnet-counters monitor -p <pid> \
    --counters System.Runtime \\
    "tiered-pgo[gc-pause,IL-Jitted,Methods-Jitted]"

工作原理

  1. 冷启动阶段:使用 JIT 快速编译所有方法

  2. 收集阶段:运行时收集热点方法、分支概率等信息

  3. 优化阶段:对热点方法进行内联、循环展开等优化

  4. 重新编译:用优化后的代码替换原始代码

性能提升

场景 性能提升
Web 服务(请求处理) 15-25%
JSON 序列化 20-40%
LINQ 查询 10-20%
字符串处理 15-30%

3. TimeProvider 抽象时间 API

TimeProvider 是 .NET 8 引入的时间抽象层,让你的代码可以轻松测试时间相关逻辑。

// 之前的做法:直接使用 DateTime.Now(难以测试)
public class ExpiryService
{
    public bool IsExpired(DateTime created) =>
        DateTime.Now - created > TimeSpan.FromHours(1);
}

// .NET 8 的做法:使用 TimeProvider(可 mock)
public class ExpiryService(TimeProvider timeProvider)
{
    public bool IsExpired(DateTime created) =>
        timeProvider.GetLocalNow().DateTime - created > TimeSpan.FromHours(1);
}

// 测试时注入假时间
var fakeTime = new FakeTimeProvider();
fakeTime.SetTime(new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero));
var service = new ExpiryService(fakeTime);

// 生产环境使用系统时间
var service = new ExpiryService(TimeProvider.System);

内置实现

  • TimeProvider.System:系统时间

  • FakeTimeProvider:测试用假时间

  • 自定义实现:支持定时器、时区等

4. SearchValues 高性能搜索

SearchValues 提供了比 IndexOf 快 2-10 倍的字符/字节搜索。

// 之前的做法
string text = "Hello, World!";
int index = text.IndexOfAny(new[] { 'W', 'r', 'l' });  // 慢

// .NET 8 的做法
var searchValues = SearchValues.Create("Wrl");
int index = text.AsSpan().IndexOfAny(searchValues);  // 快 2-10 倍

// 用于字节搜索
var byteSearch = SearchValues.Create(new byte[] { 0xFF, 0xFE });
ReadOnlySpan<byte> data = stackalloc byte[] { 1, 2, 0xFF, 3 };
int byteIndex = data.IndexOfAny(byteSearch);

// 注意:字符串数组重载需要 .NET 9+
// var stringSearch = SearchValues.Create(new[] { "error", "warning", "critical" });
// string log = "Error: File not found";
// int strIndex = log.AsSpan().IndexOfAny(stringSearch);

性能对比

数据大小 IndexOfAny SearchValues 提升
100 字节 基准 2x 2 倍
1 KB 基准 5x 5 倍
10 KB 基准 8x 8 倍
100 KB 基准 10x 10 倍

5. FrozenDictionary / FrozenSet

Frozen 集合是针对"写少读多"场景优化的不可变集合。

// 之前的做法
var dict = new Dictionary<string, int>
{
    ["key1"] = 1,
    ["key2"] = 2,
    ["key3"] = 3
}.AsReadOnly();  // 仍然是普通字典

// .NET 8 的做法
var dict = new Dictionary<string, int>
{
    ["key1"] = 1,
    ["key2"] = 2,
    ["key3"] = 3
}.ToFrozenDictionary();  // 优化后的不可变字典

// 查找性能提升 30-50%
int value = dict["key1"];  // 快速查找

// FrozenSet
var set = new HashSet<int> { 1, 2, 3 }.ToFrozenSet();
bool exists = set.Contains(1);  // 快速查找

适用场景:配置缓存、路由表、查找表等。

6. SHA-3 哈希算法

.NET 8 新增了 SHA-3 系列哈希算法,这是最新的加密标准。

using System.Security.Cryptography;

// SHA-3-256
byte[] data = Encoding.UTF8.GetBytes("Hello, World!");
byte[] hash = SHA3_256.HashData(data);
string hex = Convert.ToHexString(hash);

// SHA3-512
byte[] hash512 = SHA3_512.HashData(data);

// HMAC-SHA3-256
byte[] key = Encoding.UTF8.GetBytes("secret-key");
byte[] hmac = HMACSHA3_256.HashData(key, data);

// 增量哈希
var sha3 = IncrementalHash.CreateHash(HashAlgorithmName.SHA3_256);
sha3.AppendData(data);
sha3.AppendData(data);
byte[] finalHash = sha3.GetHashAndReset();

SHA-2 vs SHA-3

特性 SHA-2 SHA-3
设计 修改的 Merkle-Damgård Keccak 置换
安全性 安全 更高
性能 稍慢
推荐 仍然安全 新项目推荐

7. System.Text.Json 改进

.NET 8 对 System.Text.Json 进行了多项重要改进:

// 1. JSON Schema 生成
JsonSchema schema = JsonSerializerOptions.Default.CreateJsonSchema(typeof(User));
string schemaJson = schema.ToJsonString();

// 2. 枚举字符串转换
var options = new JsonSerializerOptions
{
    Converters = { new JsonStringEnumConverter() }
};
var json = JsonSerializer.Serialize(Status.Active, options);
// 输出: "Active"

// 3. UnmappedMemberHandling
options = new JsonSerializerOptions
{
    UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip
};

// 4. 自定义序列化
[JsonSerializable(typeof(User))]
public partial class UserJsonContext : JsonSerializerContext { }

性能改进

操作 .NET 7 .NET 8 提升
序列化 1.0x 1.4x 40%
反序列化 1.0x 1.3x 30%
内存分配 基准 减少 50% -

8. 验证属性改进

.NET 8 新增了多个实用的验证属性:

public class UserModel
{
    // 改进:电话号码验证(.NET 8 增强)
    [Phone]
    public string Phone { get; set; }

    // 改进:信用卡验证(.NET 8 增强)
    [CreditCard]
    public string CreditCard { get; set; }

    // 改进:枚举验证
    [EnumDataType(typeof(Status))]
    public Status Status { get; set; }

    // 改进:集合验证
    [Required]
    [MinLength(1)]
    [MaxLength(10)]
    public List<string> Tags { get; set; }

    // 改进:文件扩展名验证(.NET 8 增强)
    [FileExtensions(Extensions = "jpg,png,gif")]
    public string Avatar { get; set; }

    // 改进:Url 验证(.NET 8 增强)
    [Url]
    public string Website { get; set; }
}

public enum Status { Active, Inactive }

.NET 8 改进的验证属性

  • [Phone]:电话号码格式

  • [CreditCard]:信用卡号格式

  • [Url]:URL 格式

  • [FileExtensions]:文件扩展名

  • [EnumDataType]:枚举类型

9. ZipFile 流式压缩

.NET 8 新增了 ZipFile 的流式 API,处理大文件不再 OOM。

// 之前的做法:整个文件加载到内存
ZipFile.CreateFromDirectory("source", "output.zip");

// .NET 8 的做法:流式处理
// 压缩
using var archive = ZipArchive.Open("output.zip", ZipArchiveMode.Create);
archive.CreateEntryFromFile("large-file.bin", "large-file.bin",
    new CompressionLevel(CompressionLevel.Optimal));

// 解压
using var archive = ZipArchive.Open("output.zip", ZipArchiveMode.Read);
foreach (var entry in archive.Entries)
{
    entry.ExtractToFile($"output/{entry.Key}");
}

// 流式读取(不占用大量内存)
using var stream = File.OpenRead("large.zip");
using var archive = new ZipArchive(stream, ZipArchiveMode.Read);

10. COM 互操作源生成器

.NET 8 新增了 COM 互操作的源生成器,支持 AOT 场景。

// 自动生成 COM 互操作代码
[GeneratedComInterface]
[Guid("00000000-0000-0000-0000-000000000000")]
private partial interface IMyComInterface
{
    void DoSomething();
}

// 使用
var comObject = new MyComObject();
var interface = comObject as IMyComInterface;
interface.DoSomething();  // 编译时生成高效代码

// 支持字符串 marshaling
[GeneratedComInterface]
[Guid("00000000-0000-0000-0000-000000000001")]
private partial interface IWithString
{
    [return: MarshalUsing(typeof(StringMarshaller))]
    string GetName();
}

11. 优先队列改进

PriorityQueue 在 .NET 8 中得到了多项增强:

// 基本使用
var queue = new PriorityQueue<string, int>();
queue.Enqueue("low", 1);
queue.Enqueue("high", 10);
queue.Enqueue("medium", 5);

// 批量添加
queue.EnqueueRange(new[] {
    ("task1", 1),
    ("task2", 5),
    ("task3", 10)
});

// 获取最小元素(不移除)
if (queue.TryPeek(out string? element, out int priority))
{
    Console.WriteLine($"{element}: {priority}");
}

// 移除特定元素
queue.Remove("task1", out string? removed, out int removedPriority);

12. Vector512 硬件加速

.NET 8 新增了 512 位 SIMD 支持,充分利用现代 CPU 的向量指令。

using System.Numerics;

// 512 位向量操作
Vector512<int> vector1 = Vector512.Create(1, 2, 3, 4, 5, 6, 7, 8,
                                          9, 10, 11, 12, 13, 14, 15, 16);
Vector512<int> vector2 = Vector512.Create(16, 15, 14, 13, 12, 11, 10, 9,
                                          8, 7, 6, 5, 4, 3, 2, 1);

// 向量加法
Vector512<int> result = Vector512.Add(vector1, vector2);

// 向量乘法
Vector512<int> product = Vector512.Multiply(vector1, vector2);

// 检查硬件支持
if (Vector512.IsHardwareAccelerated)
{
    Console.WriteLine("512 位 SIMD 已启用");
}

13. DateOnly / TimeOnly 改进

.NET 8 为 DateOnly 和 TimeOnly 新增了更多解析方法:

// 新增:TryParse 静默失败
if (DateOnly.TryParse("2024-01-15", out DateOnly date))
{
    Console.WriteLine(date);
}

// 新增:ParseExact 支持多种格式
var date = DateOnly.ParseExact("01/15/2024",
    new[] { "yyyy-MM-dd", "MM/dd/yyyy", "dd/MM/yyyy" });

// 新增:FromDateTime 在指定时区
var dateOnly = DateOnly.FromDateTime(DateTime.UtcNow, TimeZoneInfo.Utc);

// 新增:TimeOnly 增量
var time = new TimeOnly(10, 30);
var later = time.AddMinutes(45);  // 11:15

14. MetadataLoadContext

MetadataLoadContext 提供了安全的反射分析能力,不会加载程序集到当前域。

// 之前的做法:加载到当前域(有副作用)
var assembly = Assembly.LoadFrom("plugin.dll");

// .NET 8 的做法:只读分析(无副作用)
var resolver = new PathAssemblyResolver(new[] {
    typeof(object).Assembly.Location,
    "plugin.dll"
});

using var context = new MetadataLoadContext(resolver);
var assembly = context.LoadFromAssemblyPath("plugin.dll");

// 安全地分析类型
foreach (var type in assembly.GetTypes())
{
    Console.WriteLine($"Type: {type.FullName}");
    foreach (var method in type.GetMethods())
    {
        Console.WriteLine($"  Method: {method.Name}");
    }
}

15. GC 并发压缩

.NET 8 改进了 GC 的并发压缩能力,减少暂停时间。

// .NET 8 默认启用并发压缩
// 可通过环境变量控制
Environment.SetEnvironmentVariable("DOTNET_GCConcurrent", "1");
Environment.SetEnvironmentVariable("DOTNET_GCHeapHardLimit", "0x80000000"); // 2GB

// 查看 GC 指标
dotnet-counters monitor -p <pid> \
    --counters System.Runtime \
    "gc-pause,gc-heap-size,gen-0-gc-count"

GC 改进

特性 .NET 7 .NET 8
并发压缩 实验性 默认启用
暂停时间 较长 减少 30-50%
内存效率 基准 提升 20%

16. JIT 编译优化

.NET 8 的 JIT 编译器进行了多项优化:

// 更好的内联决策
// .NET 8 会内联更多小方法,提升性能

// 循环优化
// .NET 8 对循环进行了更多优化(循环展开、边界检查消除等)

// 查看 JIT 编译情况
dotnet-counters monitor -p <pid> \
    --counters System.Runtime \
    "methods-jitted,il-compiled-by-jit"

17. HybridGlobalization

HybridGlobalization 结合了 ICU 库和系统 globalization API,减少容器镜像大小。

// 在项目文件中启用
// <HybridGlobalization>true</HybridGlobalization>

// 使用
var culture = new CultureInfo("zh-CN");
var text = string.Format(culture, "{0:C}", 1234.56m);  // ¥1,234.56

// 验证
bool isValid = string.Equals("café", "café",
    StringComparison.FromCulture(culture));

优势:容器镜像减少 50-70MB,启动时间更快。

18. NuGet 安全改进

.NET 8 对 NuGet 进行了多项安全改进:

<!-- 包签名验证通过 NuGet.Config 或 CI 配置 -->
<!-- NuGet.Config 示例:
<config>
  <add key="signatureValidationMode" value="require" />
</config>
-->

<!-- 扫描已知漏洞 -->
<PropertyGroup>
    <NuGetAudit>true</NuGetAudit>
    <NuGetAuditLevel>low</NuGetAuditLevel>
</PropertyGroup>

<!-- 强制执行签名 -->
<PropertyGroup>
    <NuGetRequirePackageSignature>true</NuGetRequirePackageSignature>
</PropertyGroup>

改进点

  • 漏洞扫描:自动检测已知漏洞

  • 包签名验证:确保包完整性

  • 最小权限原则:减少攻击面

19. 测试框架改进

.NET 8 改进了测试框架的多项功能:

// 改进的 Code Coverage
// dotnet test --collect:"XPlat Code Coverage"
// 生成 coverage.cobertura.xml

// 改进的测试过滤器
dotnet test --filter "Category=Integration"
dotnet test --filter "FullyQualifiedName~MyClass.Method"
dotnet test --filter "Priority=1&Category=Security"

// 改进的测试报告
dotnet test --logger "trx;LogFileName=TestResults.trx"
dotnet test --logger "html;LogFileName=TestReport.html"

新增功能

  • 测试过滤器:更灵活的测试选择

  • Code Coverage:内置覆盖率收集

  • 测试报告:HTML、TRX 格式

20. 包体积缩减(Package Trimming)

.NET 8 增强了 Package Trimming,减少了最终部署包的大小。

<!-- 启用 Package Trimming -->
<PropertyGroup>
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>partial</TrimMode>  <!-- .NET 8 新增 -->
</PropertyGroup>

<!-- 保留特定类型 -->
<ItemGroup>
    <TrimmerRootAssembly Include="MyApp" />
    <TrimmerRootDescriptor Include="trimmer.xml" />
</ItemGroup>

trimmer.xml 示例

<linker>
    <assembly fullname="MyApp" preserve="all" />
    <type fullname="MyApp.Services.UserService" preserve="all" />
</linker>

效果

应用类型 未裁剪 裁剪后 减少
控制台应用 15 MB 5 MB 67%
Web 应用 50 MB 20 MB 60%
微服务 80 MB 25 MB 69%

实战场景

Native AOT 适合的场景

// 命令行工具
class Program
{
    static async Task<int> Main(string[] args)
    {
        var parser = new CommandLineBuilder(rootCommand)
            .UseDefaults()
            .Build();

        return await parser.InvokeAsync(args);
    }
}

TimeProvider 适合的场景

// 缓存服务
public class CacheService(TimeProvider timeProvider)
{
    private readonly Dictionary<string, (object Value, DateTime Expiry)> _cache = new();

    public void Set(string key, object value, TimeSpan expiry)
    {
        _cache[key] = (value, timeProvider.GetLocalNow().DateTime.Add(expiry));
    }

    public bool IsExpired(string key)
    {
        if (!_cache.TryGetValue(key, out var entry))
            return true;
        return timeProvider.GetLocalNow().DateTime > entry.Expiry;
    }
}

SearchValues 适合的场景

// 日志解析
public static class LogParser
{
    private static readonly SearchValues<char> Separators =
        SearchValues.Create(" \t\r\n");

    public static ReadOnlySpan<string> ParseLine(string line)
    {
        var tokens = new List<string>();
        var span = line.AsSpan();

        int index;
        while ((index = span.IndexOfAny(Separators)) >= 0)
        {
            tokens.Add(span[..index].ToString());
            span = span[(index + 1)..];
        }

        if (span.Length > 0)
            tokens.Add(span.ToString());

        return tokens.ToArray();
    }
}

FrozenDictionary 适合的场景

// 路由表查找
public class Router
{
    private static readonly SearchValues<char> MethodSeparators =
        SearchValues.Create(" ");

    private readonly FrozenDictionary<string, Func<Request, Response>> _routes;

    public Router()
    {
        _routes = new Dictionary<string, Func<Request, Response>>
        {
            ["/api/users"] = HandleUsers,
            ["/api/products"] = HandleProducts
        }.ToFrozenDictionary();
    }

    public Response Route(Request request)
    {
        if (_routes.TryGetValue(request.Path, out var handler))
            return handler(request);

        return new Response { StatusCode = 404 };
    }
}

迁移建议

从 .NET 7 升级

# 1. 更新 SDK
dotnet --list-sdks

# 2. 更新项目文件
<TargetFramework>net8.0</TargetFramework>

# 3. 更新 NuGet 包
dotnet list package --outdated

# 4. 测试
dotnet test

# 5. 检查 PGO 效果
dotnet-counters monitor -p <pid> --counters System.Runtime "methods-jitted"

注意事项

  1. Native AOT 兼容性:不是所有 API 都支持 AOT

  2. 动态 PGO:默认启用,首次运行可能稍慢

  3. Frozen 集合:构建后不可修改,适合读多写少场景

  4. SearchValues:高性能但需要预分配

一句话总结

.NET 8 运行时和 SDK 的改进,让你的开发更高效、应用更快速、部署更简单。


官方文档


📦 示例代码:.NET 新特性巡礼全系列配套示例代码(含 dotnet 8/9/10)

💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐