将 Zod 移植到 C#:ZodSharp – 零分配、高性能的 .NET 模式验证库
Source: Dev.to
介绍
作为一名 .NET 开发者,我长期受到一个顽固痛点的困扰:大多数验证库高度依赖反射,导致性能缓慢且持续产生内存分配。无论是验证 API 输入、表单还是数据管道,这些开销都会累积——尤其在高吞吐场景下更为明显。
后来我发现了 Zod —— TypeScript 世界中由 colinhacks 创建的模式验证黄金标准。它流畅的 API、完整的类型推断、优秀的错误信息以及零依赖设计简直像魔法。但在 C# 中并没有类似的东西。
于是我构建了 ZodSharp —— 为现代 .NET 从零重写的完整实现,始终聚焦于 性能、零分配和开发者体验。
为什么 Zod 成为 TypeScript 的标准
- 流畅、可链式调用的 API,书写起来令人愉悦
- 自动推断的完整类型安全
- 强大的组合能力,支持复杂模式
- 丰富的转换和细化功能
- 清晰、结构化的错误报告
- 零运行时依赖
在 .NET 中,我们有 FluentValidation 等可靠选项,但它们依赖反射和表达式树——灵活性很好,却在性能关键路径上代价高昂。ZodSharp 将同样优雅的开发者体验带到 C# —— 没有分配开销。
这不是简单的移植 —— 以性能为首的重写
在忠实于 Zod 公共 API 与语义的同时,几乎所有内部细节都为 .NET 的优势重新设计。
零分配:核心设计目标
首要目标:在热点路径中验证应产生 零垃圾。基于反射的验证在每次调用时都会创建对象、闭包和临时集合。ZodSharp 完全消除了这些。
关键技术
- 验证规则作为结构体
public readonly struct MinLengthRule : IValidationRule
{
private readonly int _min;
public MinLengthRule(int min) => _min = min;
public bool IsValid(in string value) => value.Length >= _min;
}
- 使用开销极小的不可变集合(
ImmutableArray、ImmutableDictionary) ValidationResult作为结构体 —— 成功或失败时不在堆上分配ArrayPool用于任何临时缓冲区- 在性能关键段使用手动循环而非 LINQ
Span和ReadOnlySpan用于零拷贝的字符串操作
流畅 API —— 尽可能贴近 Zod
API 让 Zod 用户立刻感到熟悉:
// 几乎与 Zod TS 完全相同
var schema = Z.String()
.Min(3)
.Max(50)
.Email()
.Trim()
.ToLower();
对象模式:
var userSchema = Z.Object()
.Field("name", Z.String().Min(1))
.Field("age", Z.Number().Min(0).Max(120))
.Field("email", Z.String().Email())
.Build();
转换、细化、联合、可选、字面量 —— 全部支持。
源生成器与 DataAnnotations 集成
.NET 特有的最大收益之一:编译时模式生成。
[ZodSchema]
public class User
{
[Required, StringLength(50, MinimumLength = 3)]
public string Name { get; set; } = string.Empty;
[Range(0, 120)]
public int Age { get; set; }
[EmailAddress]
public string Email { get; set; } = string.Empty;
}
// 使用方式
var result = UserSchema.Validate(new User { /* ... */ });
无需运行时反射。
项目架构概览
ZodSharp/
├── src/ZodSharp/
│ ├── Core/ → Interfaces, base classes, results, errors
│ ├── Schemas/ → String, Number, Object, Array, Union, etc.
│ ├── Rules/ → Struct‑based validation rules
│ ├── Expressions/ → Compiled validators via Expression Trees
│ ├── Json/ → Newtonsoft.Json integration
│ ├── Optimizations/ → Zero‑allocation helpers
│ └── Z.cs → Static factory (Z.String(), Z.Object(), etc.)
├── src/ZodSharp.SourceGenerators/
│ └── ZodSchemaGenerator.cs → [ZodSchema] source generator
├── example/ → Full usage samples
└── README.md
大致的性能提升
早期基准显示相较于典型的基于反射的验证,提升幅度惊人:
| 场景 | 基于反射 | ZodSharp | 增益 |
|---|---|---|---|
| 简单字符串验证 | ~0.15 ms | ~0.01 ms | ~15 倍更快 |
| 复杂对象验证 | ~0.8 ms | ~0.05 ms | ~16 倍更快 |
| 每次验证的分配 | 多次 | 零 | 已消除 |
在负载增大时差距进一步拉大。
实际使用场景
ZodSharp 在任何需要验证性能的地方都大放异彩:
- 高吞吐量的 API(REST、gRPC、GraphQL)
- 频繁输入验证的微服务
- 实时系统
- 数据摄取管道
- 具有复杂表单的桌面/移动应用
- 任何想要拥有 Zod‑式开发体验却不想承担运行时成本的场景
已实现的功能
- 流畅的模式构建
- 完整的原始类型支持(string、number、boolean、array、object、union、literal)
- 转换(
.Trim()、.ToLower()等) - 细化(自定义验证)
- 可选 / 可空包装器
- 判别式联合
- 延迟/递归模式
- 带 DataAnnotations 的源生成器
- Newtonsoft.Json 集成
- 高级字符串规则(
.Url()、.Uuid()、.Email()、.StartsWith()…) - 高级数字规则(
.Positive()、.MultipleOf()、.Finite()…)
接下来计划
- 公共基准套件
- ASP.NET Core 模型绑定集成
- Entity Framework 验证钩子
- 更多 JSON 序列化支持(
System.Text.Json) - 增强的错误格式化
- 社区贡献
最后思考
构建 ZodSharp 的过程令人极度满足。它不仅仅是把 Zod 带到 C#——更是为 .NET 生态系统中一个真实且广泛的痛点提供了解决方案:快速、类型安全、零开销的验证。
如果你曾为反射导致的慢速而叹气,或渴望在 C# 中拥有更现代的验证体验,快去试试 ZodSharp 吧。欢迎提供反馈、点星、提交 issue 与 PR!
感谢阅读! 🚀