应文浩wenhao.ying@xiaobao100.com e25ac591a7 init easy-code
2026-04-06 07:24:24 +08:00

329 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# .NET 10 平台介绍
> **文档版本**: 1.0
> **日期**: 2026-04-05
> **所属部分**: 第一部分 — 参考文档
> **单一职责**: 说明 .NET 10 平台中与本项目直接相关的特性,不涉及具体映射细节
---
## 目录
- [1. AOT 编译与单文件发布](#1-aot-编译与单文件发布)
- [2. C# 13 语言特性](#2-c-13-语言特性)
- [3. System.Text.Json 与 AOT 友好序列化](#3-systemtextjson-与-aot-友好序列化)
- [4. Microsoft.Extensions.Hosting](#4-microsoftextensionshosting)
- [5. AssemblyLoadContext 插件隔离](#5-assemblyloadcontext-插件隔离)
- [6. System.Threading.Channels 异步任务管理](#6-systemthreadingchannels-异步任务管理)
- [7. 关键 NuGet 包清单](#7-关键-nuget-包清单)
---
## 1. AOT 编译与单文件发布
### 设计意图
原始项目通过 Bun 将所有 TypeScript 打包为单一可执行二进制,做到零依赖安装。.NET 10 的 Native AOTAhead-of-Time编译提供了对等的能力并在启动速度和内存占用上更有优势。
### PublishSingleFile
`PublishSingleFile` 将运行时和应用代码打包为一个文件,发布时无需 .NET 运行时预装。
```xml
<!-- 项目文件中的 AOT + 单文件配置 -->
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed>
<AotPublish>true</AotPublish>
<RuntimeIdentifiers>osx-arm64;osx-x64;linux-x64;linux-arm64;win-x64</RuntimeIdentifiers>
</PropertyGroup>
```
### Native AOT 的约束
AOT 编译会在发布时静态分析所有代码路径任何运行时反射都可能被裁剪trim。这意味着
- `System.Text.Json` 必须使用 Source Generator 模式,不能依赖运行时反射序列化
- 插件系统无法直接 AOT主程序集使用 AOT插件程序集在 `AssemblyLoadContext` 中通过 JIT 加载
- 需要为 `[DynamicallyAccessedMembers]` 标注所有通过反射访问的类型
### 启动性能对比
| 方式 | 冷启动时间(参考值) |
|------|-----------------|
| Bun 打包 JS | ~80ms |
| .NET 10 AOT | ~15-30ms |
| .NET 10 JIT无 AOT | ~150-300ms |
---
## 2. C# 13 语言特性
### 设计意图
TypeScript 的类型系统依赖接口、联合类型和 Zod 运行时校验。C# 13 提供了更强的编译时保证,用 record 类型替代不可变 DTO用模式匹配替代联合类型的分支处理。
### Record 类型
Record 类型天然不可变,自动生成 `Equals``GetHashCode``ToString` 和解构支持,适合表达状态快照和配置对象。
```csharp
// 不可变配置模型
public record AppSettings(
string Model,
string Theme,
bool VerboseMode,
IReadOnlyList<string> AllowedDirectories
);
// 带 required 修饰符的 init-only 属性
public record FileReadInput
{
public required string FilePath { get; init; }
public int? Offset { get; init; }
public int? Limit { get; init; }
}
```
### 模式匹配
C# 13 的模式匹配可以替代 TypeScript 中对联合类型的 `switch` + `instanceof` 处理。
```csharp
// 替代 TS 的 if (msg.type === 'tool_use') { ... }
var result = message switch
{
ToolUseMessage toolUse => HandleToolUse(toolUse),
AssistantMessage assistant => HandleAssistant(assistant),
ErrorMessage { Code: >= 500 } serverError => HandleServerError(serverError),
_ => HandleUnknown(message)
};
```
### required init 属性
`required` 关键字在编译时强制调用方必须初始化某个属性,比构造函数参数更灵活,比 Nullable 警告更明确。
```csharp
public class ToolExecutionContext
{
public required string SessionId { get; init; }
public required IPermissionEngine PermissionEngine { get; init; }
public CancellationToken CancellationToken { get; init; }
}
```
---
## 3. System.Text.Json 与 AOT 友好序列化
### 设计意图
原始项目直接使用 `JSON.parse`/`JSON.stringify`,无类型安全。`System.Text.Json` 提供高性能序列化,配合 Source Generator 完全绕过运行时反射AOT 环境下也可正常工作。
### Source Generator 模式
通过 `[JsonSerializable]` 特性,编译器在构建时生成所有序列化代码,不依赖运行时反射。
```csharp
// 声明需要序列化的类型
[JsonSerializable(typeof(ApiRequest))]
[JsonSerializable(typeof(ApiResponse))]
[JsonSerializable(typeof(McpToolCall))]
[JsonSerializable(typeof(List<ToolDefinition>))]
internal partial class AppJsonContext : JsonSerializerContext { }
// 使用时传入 context
var json = JsonSerializer.Serialize(request, AppJsonContext.Default.ApiRequest);
var response = JsonSerializer.Deserialize(json, AppJsonContext.Default.ApiResponse);
```
### 流式 JSON 处理
MCP 和 API 响应都基于流式 JSON使用 `Utf8JsonReader` 可以零拷贝解析,不需要先将整个响应体读入内存。
```csharp
// SSE 流式解析示例
await foreach (var line in sseReader.ReadLinesAsync(ct))
{
if (!line.StartsWith("data: ")) continue;
var data = line["data: ".Length..];
if (data == "[DONE]") break;
var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(data));
var chunk = JsonSerializer.Deserialize(ref reader, AppJsonContext.Default.StreamChunk);
yield return chunk;
}
```
---
## 4. Microsoft.Extensions.Hosting
### 设计意图
原始项目没有显式的 DI 容器,各模块通过模块级变量和闭包共享状态。.NET 的 `Microsoft.Extensions.Hosting` 提供了标准化的应用生命周期管理,包括 DI 注册、配置系统和 `BackgroundService`
### Host 构建
```csharp
var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((ctx, cfg) =>
{
cfg.AddJsonFile("appsettings.json", optional: true);
cfg.AddEnvironmentVariables("FREECODE_");
cfg.AddCommandLine(args);
})
.ConfigureServices((ctx, services) =>
{
services.AddCoreServices()
.AddEngine()
.AddTools()
.AddCommands()
.AddApiProviders()
.AddMcp()
.AddLsp();
})
.ConfigureLogging(logging =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Warning); // 终端应用默认不打印 Info 日志
})
.Build();
```
### BackgroundService
`BackgroundService` 是 .NET 中运行后台循环任务的标准模式,替代原始项目中的自定义异步循环。
```csharp
public class TaskSchedulerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await foreach (var task in _taskChannel.Reader.ReadAllAsync(stoppingToken))
{
_ = Task.Run(() => ExecuteTaskAsync(task, stoppingToken), stoppingToken);
}
}
}
```
---
## 5. AssemblyLoadContext 插件隔离
### 设计意图
原始项目使用 `动态 import()` 加载插件JavaScript 的模块系统天然支持按需加载。.NET 中通过 `AssemblyLoadContext` 实现对等的运行时程序集隔离并支持卸载collectible context
### 基本用法
```csharp
public class PluginLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginPath)
: base(isCollectible: true) // 可卸载
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly? Load(AssemblyName assemblyName)
{
// 优先从插件目录解析,避免与主程序集冲突
var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
return assemblyPath != null ? LoadFromAssemblyPath(assemblyPath) : null;
}
}
```
### 卸载流程
```csharp
// 加载插件
var context = new PluginLoadContext(pluginPath);
var assembly = context.LoadFromAssemblyPath(pluginPath);
var plugin = (IPlugin)Activator.CreateInstance(assembly.GetType("MyPlugin")!)!;
// 卸载插件(释放所有强引用后 GC 自动回收)
context.Unload();
```
### AOT 注意事项
主程序集使用 AOT 编译,但插件程序集在运行时通过 `AssemblyLoadContext` 以 JIT 模式加载。这是混合部署模式:主入口 AOT 启动快、内存小,插件保持 JIT 的灵活性。
---
## 6. System.Threading.Channels 异步任务管理
### 设计意图
原始项目的后台任务通过自定义队列和 Promise 链管理,存在竞态风险。`System.Threading.Channels` 是 .NET 内建的高性能生产者-消费者通道线程安全背压backpressure支持完善。
### Channel 基本模式
```csharp
// 创建无界通道
var channel = Channel.CreateUnbounded<BackgroundTask>(new UnboundedChannelOptions
{
SingleWriter = false,
SingleReader = true // 单消费者简化并发模型
});
// 生产者(任意线程)
await channel.Writer.WriteAsync(new LocalShellTask { Command = "npm test" }, ct);
// 消费者BackgroundService 中)
await foreach (var task in channel.Reader.ReadAllAsync(ct))
{
await ProcessTaskAsync(task, ct);
}
```
### 有界通道与背压
```csharp
// 有界通道:满了就等待(背压)
var bounded = Channel.CreateBounded<ApiRequest>(new BoundedChannelOptions(capacity: 100)
{
FullMode = BoundedChannelFullMode.Wait
});
```
---
## 7. 关键 NuGet 包清单
以下是本项目直接依赖的 NuGet 包,版本号为设计时目标版本。
| 包名 | 版本 | 用途 |
|------|------|------|
| `Terminal.Gui` | 2.* | 终端 TUI 框架,替代 React+Ink |
| `Spectre.Console` | 0.* | 富文本输出:表格、进度条、面板 |
| `System.CommandLine` | 2.* | CLI 参数解析,替代 Commander.js |
| `FluentValidation` | 12.* | POCO 对象校验,替代 Zod |
| `JsonSchema.Net` | 7.* | JSON Schema 生成与校验 |
| `Refit` | 8.* | 强类型 HTTP 客户端 |
| `Polly` | 8.* | 重试、熔断、超时策略 |
| `StreamJsonRpc` | 2.* | JSON-RPC 2.0 实现,用于 MCP 协议 |
| `OmniSharp.Extensions.LanguageServer.Client` | 0.* | LSP 客户端 |
| `Grpc.Net.Client` | 2.* | gRPC 客户端(部分 API 提供商) |
| `Microsoft.Identity.Client` (MSAL) | 4.* | OAuth/OIDC 认证 |
| `IdentityModel.OidcClient` | 6.* | OIDC 客户端流程 |
| `AWSSDK.BedrockRuntime` | 4.* | AWS Bedrock 流式 API |
| `Google.Apis.Auth` | 1.* | Google Cloud 认证Vertex AI |
| `Microsoft.AspNetCore.SignalR.Client` | 10.* | WebSocket 高层抽象 |
| `Markdig` | 0.* | Markdown 渲染 |
| `DiffPlex` | 1.* | diff 计算与展示 |
| `SixLabors.ImageSharp` | 3.* | 图片处理 |
| `PdfPig` | 0.* | PDF 文本提取 |
| `YamlDotNet` | 16.* | YAML 解析 |
| `Microsoft.Extensions.Hosting` | 10.* | DI + 生命周期管理 |
| `System.Threading.Channels` | 10.* | 生产者-消费者通道 |
| `xunit` | 2.* | 单元测试框架 |
| `NSubstitute` | 5.* | Mock 框架 |
| `FluentAssertions` | 7.* | 断言库 |