.NET 11 Preview 4 深度解析:Runtime-Async 革命、进程 API 重写与 AI 原生 SDK 的全面进化
2026年5月12日,微软发布了 .NET 11 Preview 4。这不是一次修修补补的常规更新——Runtime-Async 全面铺开、Process API 从"八步走"到一行搞定、MCP Server 模板直接内置 SDK、Blazor 电路暂停、EF Core 向量搜索……每一个改动都在回答同一个问题:.NET 如何在 AI 时代保持企业级开发平台的生命力?
本文从底层运行时到上层应用框架,逐一拆解 Preview 4 的核心技术变更,配以完整代码示例和架构分析,帮你判断哪些特性值得现在就关注、哪些需要观望。
一、背景:.NET 11 的战略定位
在聊技术细节之前,先理解 .NET 11 在微软产品线中的位置。
.NET 每年一个正式版,11.0 计划在 2026 年 11 月 GA。从 Preview 1 到 Preview 4,我们能清晰看到三个战略方向:
- 运行时现代化:Runtime-Async 是 .NET 异步编程模型自 async/await 引入以来最大的底层重构
- AI 原生:MCP Server 模板内置、EF Core 向量搜索、ASP.NET Core AI 集成——AI 不再是"附加功能"
- 开发者体验:Process API 重写、dotnet watch 跨平台热重载、配置绑定增强——让日常开发更顺手
Preview 4 是一个"深度足够、覆盖面广"的版本,没有花哨的大噱头,但每个改动都指向实际痛点。下面按领域逐一深入。
二、Runtime-Async:.NET 异步编程的底层革命
2.1 什么是 Runtime-Async?
从 C# 5.0 引入 async/await 开始,编译器通过生成 异步状态机(Async State Machine) 来实现异步。每次你写一个 async 方法,编译器都会在背后生成一个 struct,包含状态字段、awaiter、moveNext 委托等一堆样板代码。这种方式工作得很好,但有代价:
- 方法体积膨胀:每个 async 方法都会生成额外的 IL 代码
- 调试复杂度:async 栈帧是合成的,调试器看到的调用栈和实际执行路径不一致
- AOT 兼容性:状态机的复杂跳转模式让 NativeAOT 的优化空间受限
Runtime-Async 的核心思路是:把状态机的生成从编译期移到运行时。
Preview 4 的标志性变更是:整个 .NET 运行时库和 ASP.NET Core 共享框架全部使用 runtime-async=on 编译。这意味着框架层所有的异步方法(FileStream.ReadAsync、HttpClient.GetAsync、DbContext.SaveChangesAsync 等)都不再生成传统的编译器状态机,而是依赖运行时原语直接调度。
2.2 对开发者意味着什么?
好消息是:你的代码不需要任何改动。async/await 语法不变,API 签名不变,行为语义不变。变化全部发生在底层。
但你能获得:
吞吐量提升:运行时调度比编译器生成的状态机更高效。框架方法(你每天都在调用但看不见实现的方法)的异步路径被优化了。
库体积缩减:不再为每个 async 方法生成状态机 IL,意味着框架 DLL 更小。对于 NativeAOT 场景(单文件发布、容器镜像),这直接转化为更小的二进制。
调试体验改善:运行时调度的栈帧更清晰,异步调试的"栈帧跳跃"问题有望缓解。
2.3 配套优化
Runtime-Async 不是孤立启用的,Preview 4 还带来了两个配套优化:
协变 Task → Task<T> 重写
// 基类
public abstract class BaseService
{
public virtual Task ProcessAsync() { /* ... */ }
}
// 派生类 - 返回更具体的 Task<string>
public class MyService : BaseService
{
public override Task<string> ProcessAsync() { /* ... */ }
}
以前这种协变返回需要编译器生成桥接代码。现在运行时自动处理,零开销。
Crossgen2 内联 Runtime-Async 方法
预编译(ReadyToRun/AOT)阶段现在可以对 runtime-async 方法执行内联优化。这减少了间接调用开销,对热路径性能至关重要。
2.4 需要注意的风险
如果你在 Preview 4 上遇到:
- AsyncLocal 的行为与之前不一致
- 异常栈帧的格式发生变化
- 某些 async 方法在特定边界条件下表现异常
这很可能是 Runtime-Async 的边界问题。微软团队明确表示这是重点关注领域,遇到问题应该积极反馈。
2.5 实战:体验 Runtime-Async
目前 Runtime-Async 对开发者是透明的——你不需要修改任何代码。但你可以对比 Preview 3 和 Preview 4 的性能来验证收益:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
BenchmarkRunner.Run<AsyncBenchmark>();
[MemoryDiagnoser]
public class AsyncBenchmark
{
private readonly HttpClient _http = new();
[Benchmark]
public async Task<string> FetchDataAsync()
{
var response = await _http.GetAsync("https://httpbin.org/get");
return await response.Content.ReadAsStringAsync();
}
}
用 BenchmarkDotNet 分别在 .NET 10 和 .NET 11 Preview 4 上跑这个基准测试,关注 Allocated Memory 和 Throughput 的差异——框架层的异步优化会体现在这里。
三、Process API 重写:从"八步走"到一行搞定
3.1 旧 API 的痛点
任何一个写过 CLI 工具、构建脚本、DevOps 自动化的 .NET 开发者,都经历过启动子进程的痛苦:
// 旧写法 - "八步走"
var psi = new ProcessStartInfo("git", "status")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var process = Process.Start(psi)!;
var stdout = new StringBuilder();
var stderr = new StringBuilder();
process.OutputDataReceived += (s, e) =>
{
if (e.Data != null) stdout.AppendLine(e.Data);
};
process.ErrorDataReceived += (s, e) =>
{
if (e.Data != null) stderr.AppendLine(e.Data);
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await process.WaitForExitAsync();
if (process.ExitCode != 0)
throw new Exception($"git failed: {stderr}");
八行配置 + 三行事件订阅 + 两行启动读取 + 一行等待 + 错误处理……就为了执行一个 git status。每次写都像在受刑。
3.2 新 API:一行搞定
Preview 4 彻底重写了进程管理 API:
// 新写法 - 一行搞定
var result = await Process.RunAndCaptureTextAsync("git", "status", "--porcelain");
Console.WriteLine($"Exit Code: {result.ExitStatus.ExitCode}");
Console.WriteLine($"Stdout:\n{result.StandardOutput}");
Console.WriteLine($"Stderr:\n{result.StandardError}");
从 15+ 行缩减到 3 行。这就是好的 API 设计——把常见的 80% 用法简化到极致,同时保留底层 API 给需要精细控制的 20% 场景。
3.3 完整 API 清单
| API | 说明 |
|---|---|
Process.Run() / Process.RunAsync() | 启动进程并等待退出,返回 ProcessExitStatus |
Process.RunAndCaptureText() / ...Async() | 启动 + 捕获 stdout/stderr 为文本 |
Process.ReadAllText() / Process.ReadAllBytes() | 启动 + 读取全部输出 |
Process.ReadAllLinesAsync() | 逐行流式读取,返回 ProcessOutputLine(区分 stdout/stderr) |
Process.StartAndForget() | 启动后立即返回,自动 detach 句柄 |
ProcessStartInfo.KillOnParentExit (Windows) | 父进程退出时自动杀子进程 |
ProcessStartInfo.StartDetached | 子进程脱离父进程会话独立运行 |
3.4 实战:构建一个轻量级任务运行器
新 API 特别适合写任务运行器和 CLI 工具。下面是一个实用示例:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
public class TaskRunner
{
public record TaskResult(string Name, int ExitCode, string Output, double DurationMs);
public async Task<TaskResult> RunAsync(string name, string command, params string[] args)
{
var sw = Stopwatch.StartNew();
await foreach (var line in Process.ReadAllLinesAsync(command, args))
{
var prefix = line.IsError ? "[ERR] " : "[OUT] ";
Console.WriteLine($"{prefix}[{name}] {line.Content}");
}
sw.Stop();
// 重新获取退出码
var result = await Process.RunAsync(command, args);
return new TaskResult(name, result.ExitStatus.ExitCode, "", sw.Elapsed.TotalMilliseconds);
}
public async Task RunPipelineAsync(params (string name, string cmd, string[] args)[] tasks)
{
foreach (var (name, cmd, args) in tasks)
{
Console.WriteLine($"\n▶ Running: {name}");
var result = await RunAsync(name, cmd, args);
if (result.ExitCode != 0)
{
Console.WriteLine($"❌ {name} failed with exit code {result.ExitCode}");
return;
}
Console.WriteLine($"✅ {name} completed in {result.DurationMs:F0}ms");
}
Console.WriteLine("\n🎉 All tasks completed successfully!");
}
}
// 使用示例
var runner = new TaskRunner();
await runner.RunPipelineAsync(
("Build", "dotnet", new[] { "build", "--configuration", "Release" }),
("Test", "dotnet", new[] { "test", "--no-build" }),
("Publish", "dotnet", new[] { "publish", "-c", "Release", "-o", "./publish" })
);
这个模式在 CI/CD 脚本、本地开发工具链中非常常见。以前写这样的工具需要大量样板代码,现在核心逻辑清晰可见。
3.5 StartDetached:真正的后台进程
// 启动一个独立于当前终端的后台进程
var psi = new ProcessStartInfo("dotnet", "run")
{
StartDetached = true
};
Process.Start(psi);
// 即使当前进程退出,dotnet run 也会继续运行
这在写 daemon、后台工作进程、自更新程序时非常有用。以前实现这个需要 P/Invoke 或复杂的会话管理。
四、MCP Server 模板内置 SDK:.NET 拥抱 AI Agent 标准
4.1 MCP 是什么?
MCP(Model Context Protocol)是 Anthropic 提出的开放协议,用于连接 AI 模型与外部工具和数据源。简单理解:
AI 客户端(Claude、GPT、Codex…)
↕ MCP 协议
MCP Server(你写的)
↕ HTTP/gRPC/本地调用
你的业务系统(数据库、API、文件系统…)
要让 AI 调用你的系统,不需要写 10 个不同 AI 平台的集成——写一个 MCP Server 就够了。OpenAI、Anthropic、Cursor、Windsurf 等主流 AI 客户端都已经或正在支持 MCP。
4.2 一行命令创建 MCP Server
Preview 4 把 MCP Server 模板直接内置到 .NET SDK:
dotnet new mcpserver -o MyAgentService
cd MyAgentService
dotnet run
生成的项目包含:
- MCP 协议的完整实现(stdio 和 SSE transport)
- 工具(Tool)注册的脚手架代码
- 配置文件和调试支持
以前需要额外安装 Microsoft.McpServer.ProjectTemplates NuGet 包,现在是开箱即用——和 console、webapi、blazorserver 平起平坐。
4.3 实战:写一个数据库查询 MCP Server
// Tools/DatabaseTools.cs
using ModelContextProtocol.Server;
public class DatabaseTools
{
private readonly IDbConnectionFactory _dbFactory;
public DatabaseTools(IDbConnectionFactory dbFactory)
{
_dbFactory = dbFactory;
}
[McpServerToolType]
public static class Queries
{
[McpServerTool, Description("查询用户订单列表")]
public static async Task<string> GetOrders(
[Description("用户ID")] string userId,
[Description("最大返回数量")] int limit = 20)
{
// 实际项目中注入服务,这里简化
await using var db = /* _dbFactory.CreateConnection() */;
var orders = await db.QueryAsync<Order>(
"SELECT * FROM orders WHERE user_id = @userId ORDER BY created_at DESC LIMIT @limit",
new { userId, limit });
return JsonSerializer.Serialize(orders, new JsonSerializerOptions
{
WriteIndented = true
});
}
[McpServerTool, Description("获取订单统计信息")]
public static async Task<string> GetOrderStats(
[Description("开始日期")] string startDate,
[Description("结束日期")] string endDate)
{
// 统计逻辑...
return """{"total": 1024, "completed": 980, "pending": 44}""";
}
}
}
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer();
var app = builder.Build();
app.MapMcpServer();
app.Run();
AI 客户端连接后,就能直接调用 GetOrders 和 GetOrderStats 这两个工具——不需要写任何额外的 API 适配层。
4.4 这一步的战略意义
MCP Server 模板内置 SDK 看起来是个小改动,但信号意义很大:
- 微软在选边站:没有搞封闭的 AI 集成方案,而是拥抱 MCP 这个开放协议
- 从 .NET 8 的 Microsoft.Extensions.AI 到 Semantic Kernel 到现在的 MCP 模板,一条清晰的 AI 生态建设路径
- 对于企业开发者:如果你有大量 .NET 后端系统想接入 AI Agent,现在门槛接近于零
五、Blazor:五个让生产环境体验飞跃的改进
5.1 Virtualize 终于不抖了
用过 <Virtualize> 做长列表的人都知道一个老毛病:当列表上方的某个元素高度变化(展开/折叠、异步加载完成),视口中的内容会突然跳动。这个 bug 困扰了 Blazor 开发者好几个版本。
Preview 4 用浏览器原生的 scroll anchoring 彻底解决了这个问题。不需要改任何代码,升级到 Preview 4 后自动修复。
5.2 AnchorMode:聊天界面的福音
新增的 AnchorMode 参数专门解决"列表前后插入数据时视口怎么处理":
@* 聊天界面:新消息追加时自动滚到底部 *@
<Virtualize Items="@messages"
AnchorMode="VirtualizeAnchorMode.End"
ItemComparer="@_byId">
<ItemContent Context="msg">
<div class="message">@msg.Text</div>
</ItemContent>
</Virtualize>
@* 新闻流:新内容插入顶部时保持当前视口 *@
<Virtualize Items="@articles"
AnchorMode="VirtualizeAnchorMode.Beginning">
<!-- ... -->
</Virtualize>
以前做聊天 UI 要写一堆 JS 手动控制滚动位置,现在一个属性搞定。这是一个"用过就回不去"的改进。
5.3 服务器端电路暂停
// 服务端主动暂停 Blazor 电路
await circuit.Features.Get<IHostCircuitFeature>()
.RequestCircuitPauseAsync();
实际场景:服务器负载过高时,暂时断开非活跃用户的 Blazor 连接释放资源。用户切回页面时瞬间恢复,不会感知到中断。
对大规模 Blazor Server 部署(比如企业内部管理系统),这是运维层面的关键能力。
5.4 SupplyParameterFromTempData
@page "/order/confirm"
<StatusMessage />
@code {
[SupplyParameterFromTempData]
public string? StatusMessage { get; set; }
}
"提交后重定向再显示成功消息"这个极其常见的场景,终于不用再写 cookie hack 了。值在重定向上一次请求中自动保存和恢复。
5.5 Blazor Web Worker 改进
Web Worker 模板新增了两个关键能力:
// 前端 JS 调用 Worker(fire-and-forget)
await worker.invokeVoidAsync('processIn', data);
// 支持 CancellationToken 和超时
await worker.invokeAsync('longRunningOp', data,
cancellationToken,
TimeSpan.FromSeconds(30));
卡住的 Worker 可以干净地终止,不会再阻塞主线程。
六、Span-based 压缩 API:高性能场景的新选择
6.1 新 API 概览
Preview 4 新增了基于 Span<T> / ReadOnlySpan<T> 的压缩和解压 API:
using System.IO.Compression;
// 压缩 - 零中间分配
byte[] source = GetLargeData();
byte[] compressed = new byte[ZLibCompressor.GetMaxCompressedLength(source.Length)];
int compressedLength = ZLibCompressor.Compress(source, compressed);
// 解压
byte[] decompressed = new byte[originalLength];
int decompressedLength = ZLibDecompressor.Decompress(
compressed.AsSpan(0, compressedLength),
decompressed);
6.2 支持的格式
| 格式 | 编码器 | 解码器 |
|---|---|---|
| Deflate | DeflateCompressor | DeflateDecompressor |
| ZLib | ZLibCompressor | ZLibDecompressor |
| GZip | GZipCompressor | GZipDecompressor |
6.3 与旧 API 的对比
// 旧方式 - 有中间分配
using var outputStream = new MemoryStream();
using (var gzipStream = new GZipStream(outputStream, CompressionLevel.Optimal))
{
await gzipStream.WriteAsync(source);
}
byte[] compressed = outputStream.ToArray(); // 这里有一次 ToArray 分配
// 新方式 - 零分配(除了输入输出缓冲区)
int written = GZipCompressor.Compress(source, compressed);
对于网络中间件、消息队列消费者、日志处理管道等高吞吐场景,减少 GC 压力意味着更高的稳定吞吐量。
6.4 实战:高性能 HTTP 响应压缩中间件
public class SpanCompressionMiddleware
{
private readonly RequestDelegate _next;
public SpanCompressionMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context)
{
var originalBody = context.Response.Body;
using var compressedStream = new MemoryStream();
context.Response.Body = compressedStream;
await _next(context);
// 获取原始响应
compressedStream.Position = 0;
var originalBytes = compressedStream.ToArray();
// 使用 Span API 压缩
var maxLen = GZipCompressor.GetMaxCompressedLength(originalBytes.Length);
var compressed = new byte[maxLen];
int compressedLen = GZipCompressor.Compress(originalBytes, compressed);
// 设置响应头并写入压缩后的内容
context.Response.Headers.ContentEncoding = "gzip";
context.Response.ContentLength = compressedLen;
context.Response.Body = originalBody;
await originalBody.WriteAsync(compressed.AsMemory(0, compressedLen));
}
}
七、EF Core:SQL Server 向量搜索与 JSON 集成
7.1 SQL Server 2025 近似向量搜索
这是 EF Core 在 AI 时代的最重要的新能力——直接用 LINQ 查询向量数据库:
// 定义向量列
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public Vector Embedding { get; set; } = null!;
}
// 语义搜索 - 找最相似的产品
float[] queryVector = await embeddingGenerator.GenerateAsync("高性能跑步鞋");
var similarProducts = await context.Products
.OrderBy(p => p.Embedding.ApproximateDistance(queryVector))
.Take(10)
.ToListAsync();
// 带过滤条件的语义搜索
var results = await context.Products
.Where(p => p.Category == "运动" && p.Price < 1000)
.OrderBy(p => p.Embedding.ApproximateDistance(queryVector))
.Take(5)
.Select(p => new { p.Name, p.Price, Distance = p.Embedding.ApproximateDistance(queryVector) })
.ToListAsync();
7.2 JSON 映射集成到关系模型
// 实体定义 - JSON 列完全融入关系模型
public class Order
{
public int Id { get; set; }
public List<OrderItem> Items { get; set; } = new(); // JSON 列
public Dictionary<string, string> Metadata { get; set; } = new(); // JSON 列
public DateTime CreatedAt { get; set; }
}
// LINQ 查询 - JSON 字段可以像普通属性一样查询
var recentOrders = await context.Orders
.Where(o => o.Items.Count > 3) // JSON 数组长度过滤
.Where(o => o.Metadata["source"] == "mobile") // JSON 字段过滤
.OrderByDescending(o => o.CreatedAt)
.ToListAsync();
JSON 列不再是"二等公民"——它们完全融入查询、跟踪和迁移管道。这意味着你可以在关系模型中灵活使用 JSON 存储半结构化数据,同时享受强类型的 LINQ 查询体验。
7.3 时态表支持
public class AuditRecord
{
public int Id { get; set; }
public string Content { get; set; } = "";
public DateTime PeriodStart { get; set; } // 自动映射
public DateTime PeriodEnd { get; set; } // 自动映射
}
// 查询某个时间点的数据
var historicalData = await context.AuditRecords
.TemporalAsOf(specificDateTime)
.Where(r => r.Content.Contains("error"))
.ToListAsync();
SQL Server 时态表的 PeriodStart/PeriodEnd 列现在可以显式映射到 CLR 属性,配合 TemporalAsOf 查询,实现完整的数据历史追溯。
八、JIT 编译器与运行时优化
8.1 常量折叠 SequenceEqual
// JIT 在编译期就能确定结果
if ("hello".SequenceEqual("hello")) // 直接优化为 true
{
// 这个分支在 JIT 后是确定会执行的
}
// 运行时比较也能受益
const string Version = "2.0.0";
if (header.SequenceEqual(Version)) // 编译期折叠
这不是什么惊天动地的优化,但在热路径上频繁调用的字符串比较场景(比如协议解析、路由匹配),累积效果可观。
8.2 SIMD 代码生成改进
JIT 编译器对 SIMD(Single Instruction Multiple Data)的代码生成持续改进,这意味着:
Vector256<T>和Vector512<T>的操作生成更高效的 SIMD 指令- 内存对齐处理更智能
- 跨平台 SIMD 行为一致性更好
对于图像处理、加密、数值计算等场景,这可能带来 2-5x 的性能提升。
8.3 支持 1024+ CPU
.NET 之前有 64 个 CPU 组的限制,在超过 1024 核的机器上会出问题。Preview 4 打破了限制,面向云原生的超大实例(AWS u-* 系列实例、Azure HBv4 系列等)。
绝大多数人用不上,但这说明 .NET 在认真对待高性能计算场景。
九、SDK 和工具链改进
9.1 dotnet watch 支持移动开发热重载
# 交互式选择目标设备
dotnet watch --device
# 输出类似:
# ? Select a device to run on:
# > 📱 Pixel 8 (Android 14) - emulator-5554
# 📱 iPhone 15 Pro (iOS 17.5) - simulator-xxxx
# 🖥️ macOS
修改 XAML 或 C# 代码后,变更自动推送到运行中的模拟器或物理设备。这让 MAUI 开发的迭代速度大幅提升——终于不再需要每次修改都重新编译部署了。
9.2 Fish Shell 补全
# Fish 用户现在也有命令补全了
dotnet <TAB> # 列出所有子命令
dotnet build <TAB> # 列出 build 选项
之前只有 Bash、Zsh、PowerShell 有补全支持。Fish 用户终于不用羡慕了。
9.3 FORCE_COLOR 支持
# 管道操作中保留彩色输出
FORCE_COLOR=1 dotnet run | tee build.log
以前 dotnet run | tee 这种管道操作会让颜色消失。现在遵循业界标准的 FORCE_COLOR 环境变量。
9.4 OpenTelemetry 遥测迁移
.NET CLI 内部遥测从 Application Insights 迁移到 OpenTelemetry。这不是用户直接能感知的改动,但说明微软在内部也在拥抱可观测性标准。
十、HTTP QUERY 方法:复杂搜索的优雅解决方案
10.1 问题背景
REST API 设计中有一个经典的两难:
- 用 GET:搜索条件太多时 URL 会很长,可能超过浏览器和服务器的 URL 长度限制
- 用 POST:语义不对,POST 应该用于创建资源,不是查询
HTTP QUERY 方法就是为此而生的——它是一个安全的幂等方法,但允许在请求体中发送查询条件:
// 注册 HTTP QUERY 端点
app.MapMethods("/api/products/search", ["QUERY"], async (
[FromBody] ProductSearchRequest request,
ProductDbContext db) =>
{
var query = db.Products.AsQueryable();
if (!string.IsNullOrEmpty(request.Keyword))
query = query.Where(p => p.Name.Contains(request.Keyword));
if (request.MinPrice.HasValue)
query = query.Where(p => p.Price >= request.MinPrice.Value);
if (request.MaxPrice.HasValue)
query = query.Where(p => p.Price <= request.MaxPrice.Value);
if (request.CategoryIds?.Length > 0)
query = query.Where(p => request.CategoryIds.Contains(p.CategoryId));
// ... 更多过滤条件
return Results.Ok(await query
.OrderBy(p => p.RelevanceScore)
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.ToListAsync());
});
public record ProductSearchRequest(
string? Keyword,
decimal? MinPrice,
decimal? MaxPrice,
int[]? CategoryIds,
string? SortBy,
SortDirection SortDirection,
int Page = 1,
int PageSize = 20
);
10.2 OpenAPI 支持
ASP.NET Core 的 OpenAPI 文档生成器现在能正确识别 HTTP QUERY 方法,Swagger UI 和其他 API 文档工具可以正常展示这类端点。
10.3 客户端调用
// fetch
fetch('/api/products/search', {
method: 'QUERY',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ keyword: '蓝牙耳机', minPrice: 100, maxPrice: 500 })
});
// HttpClient (C#)
var response = await client.SendAsync(new HttpRequestMessage(
new HttpMethod("QUERY"),
"/api/products/search")
{
Content = JsonContent.Create(new { Keyword = "蓝牙耳机", MinPrice = 100 })
});
十一、其他值得关注的改进
11.1 浮点数十六进制格式化
// IEEE 754 十六进制表示
double pi = Math.PI;
Console.WriteLine(pi.ToString("X16")); // 0x1.921fb54442d18p+1
// 解析回来
double parsed = double.Parse("0x1.921fb54442d18p+1",
NumberStyles.HexNumber, CultureInfo.InvariantCulture);
这在科学计算、二进制数据交换协议中很实用——精确表示浮点数的二进制形式,没有十进制转换的精度损失。
11.2 F# 联合类型 JSON 序列化
// F# 定义
type Shape =
| Circle of radius: float
| Rectangle of width: float * height: float
// C# 端直接反序列化,不需要自定义 converter
var shape = JsonSerializer.Deserialize<Shape>(json);
F# 和 C# 混合开发的项目直接受益。
11.3 MemoryCache 内置 OpenTelemetry 指标
services.AddMemoryCache()
.AddOpenTelemetry(); // 自动暴露缓存指标
暴露的指标包括:
dotnet.cache.requests(带 hit/miss 标签)dotnet.cache.evictionsdotnet.cache.entriesdotnet.cache.estimated_size
Grafana 仪表盘直接接,不用再自己包一层指标收集。
11.4 Kestrel TLS 握手故障诊断
// 以前 TLS 握手失败就是个 IOException
// 现在能拿到具体原因
app.Run(async (context) =>
{
var tlsFeature = context.Features.Get<ITlsHandshakeFeature>();
if (tlsFeature != null && tlsFeature.Exception != null)
{
logger.LogWarning(tlsFeature.Exception,
"TLS handshake failed: {Reason}", tlsFeature.Exception.Message);
}
});
线上排查 SSL/TLS 问题终于不用瞎猜了。
11.5 ConfigurationIgnore 属性
public class AppOptions
{
public string Endpoint { get; set; } = "";
[ConfigurationIgnore] // 这个属性不参与配置绑定
public string ComputedKey => Endpoint + ":default";
}
比以前用各种 hack 排除计算属性干净多了。
11.6 HttpClient HTTP/2 自动降级
在 Windows 认证(NTLM/Negotiate)场景下,HttpClient 自动从 HTTP/2 降级到 HTTP/1.1。因为 Windows 认证机制不支持 HTTP/2 多路复用,以前会直接报错。企业内网应用的福音。
十二、性能优化建议
基于 Preview 4 的变化,以下是一些实际可用的性能优化建议:
12.1 迁移到 Span 压缩 API
如果你有高频的压缩/解压操作(日志管道、消息队列、HTTP 中间件),从 GZipStream 迁移到 GZipCompressor.Compress():
// Before: 每次 ~200ns + 分配
using var ms = new MemoryStream();
using (var gz = new GZipStream(ms, CompressionLevel.Fastest))
gz.Write(data);
var result = ms.ToArray();
// After: 每次 ~80ns + 零中间分配
var buffer = new byte[GZipCompressor.GetMaxCompressedLength(data.Length)];
int len = GZipCompressor.Compress(data, buffer);
12.2 利用 MemoryCache 内置指标
不再需要自己写缓存监控:
// 只需配置 OpenTelemetry 导出
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics => metrics
.AddMeter("Microsoft.Extensions.Caching.Memory"));
// Prometheus/Grafana 自动拿到缓存命中率等指标
12.3 审查 async 方法的性能
升级到 Preview 4 后,用 BenchmarkDotNet 重新测量你的热路径。Runtime-Async 的优化可能让你的高吞吐场景(比如消息消费者、API 网关)自动获得 5-15% 的吞吐量提升。
12.4 1024+ CPU 场景
如果你在 AWS/Azure 的大实例上运行 .NET,升级到 Preview 4 可以解除 CPU 核数限制。
十三、迁移指南
13.1 升级步骤
# 安装 .NET 11 Preview 4 SDK
# macOS
brew install dotnet@11-preview
# 或直接下载
# https://dotnet.microsoft.com/download/dotnet/11.0
# 更新 global.json
dotnet new globaljson --sdk-version 11.0.100-preview.4.xxxxx
# 更新 TargetFramework
# <TargetFramework>net11.0</TargetFramework>
# 升级 NuGet 包
dotnet list package --outdated
dotnet add package Microsoft.AspNetCore.App --version 11.0.0-preview.4.*
dotnet add package Microsoft.EntityFrameworkCore --version 11.0.0-preview.4.*
13.2 可能的破坏性变更
- Runtime-Async 行为差异:极少数情况下 async 方法的行为可能与之前版本不同,特别是在复杂的异常处理和 AsyncLocal 场景
- Process API 变更:新增的 API 是增量添加的,旧 API 仍然可用,不存在破坏性变更
- Blazor 虚拟化:scroll anchoring 行为变化可能在极少数自定义滚动方案中需要调整
13.3 不建议立即升级的场景
- 生产环境的稳定运行系统——等 GA
- 对异步行为有精细依赖的系统——等 Runtime-Async 更成熟
- MAUI 生产应用——iOS 热重载还有已知限制
十四、总结与展望
14.1 Preview 4 的核心信号
回顾整个 Preview 4,三个趋势非常清晰:
第一,运行时现代化是大方向。 Runtime-Async 的全面铺开不是一个小优化——它代表了 .NET 团队对异步编程模型的一次深层次反思和重构。从编译器状态机到运行时调度,这个转变的完成度将在后续预览版中逐步显现。
第二,AI 是一等公民。 MCP Server 模板内置 SDK、EF Core 向量搜索、ASP.NET Core AI 集成——这些不是零散的实验性功能,而是一条完整的 AI 开发者体验路线图。.NET 在 AI 时代的定位很明确:企业级 AI 应用的最佳运行平台。
第三,开发者体验是持续的优先级。 Process API 从"八步走"到一行搞定、Virtualize 不抖了、dotnet watch 支持移动热重载——这些"小"改进直接影响每天写代码的体验。积少成多,这就是让人愿意继续用 .NET 的理由。
14.2 对不同角色的建议
| 角色 | 应该关注什么 |
|---|---|
| 后端开发者 | Runtime-Async 性能、Process API、Span 压缩 |
| AI 工程师 | MCP Server 模板、EF Core 向量搜索 |
| 前端/全栈 | Blazor Virtualize 修复、AnchorMode |
| 移动开发者 | MAUI dotnet watch 热重载 |
| DevOps | 1024+ CPU 支持、OpenTelemetry 指标 |
| 架构师 | .NET AI 战略布局、HTTP QUERY 标准 |
14.3 展望 GA
.NET 11 预计在 2026 年 11 月正式发布。从 Preview 4 的成熟度来看,核心特性基本定型。建议在 Preview 5/6 阶段重点关注:
- Runtime-Async 的性能数据和稳定性
- NativeAOT 与 Runtime-Async 的配合效果
- EF Core 向量搜索的 LINQ 表达式完善度
- MAUI 热重载的 iOS 限制是否解除
Preview 4 已经展现出相当成熟的技术方向,值得开始评估和试用。尤其是 Process API 和 MCP Server 模板,这两个特性现在就能在生产项目中使用——前者解决的是每天都会遇到的痛点,后者为即将到来的 AI Agent 爆发做好准备。