TypedSql:在 C# 类型系统上实现一个 SQL 查询引擎
NET 里写查询的时候很多场景下数据其实早就都在内存里了不是数据库连接也不是某个远程服务的结果而就是一个数组或者 List。我只是想过滤一下、投影一下。这时候通常有几种选择写一个 foreach 循环 —— 性能好、可控但代码稍微有点啰嗦用 LINQ —— 写起来舒服看起来也优雅就是有迭代器、委托带来的那点开销要么干脆极端一点把数据塞进数据库再写真正的 SQL这听起来就有点反直觉……但是我想尝试一条完全不同的思路如果我们把 C# 的类型系统本身当成查询计划会怎样也就是说不是像平时那样在运行时构建一棵表达式树再拿着这棵树去解释执行整个查询而是写一段 SQL 风格的字符串把它编译成一个类型这个类型从头到尾描述了整个查询管道然后所有实际运行时的逻辑都走静态方法。这个想法最终促成了 TypedSql —— 一个用 C# 类型系统实现的内存内 SQL 查询引擎。1000016101把查询变成嵌套的泛型类型#TypedSql 的核心想法看上去非常简单一个查询其实可以是一串嵌套的泛型类型比如 WhereSelectTRow, …, Stop… 这样。顺着这个想法再往下推几步会自然落到一套具体的设计上。把执行计划塞进类型系统#在 TypedSql 里每一个编译好的查询最终都会变成一个封闭的泛型管道类型。这个管道是由一些基础节点拼出来的比如WhereTRow, TPredicate, TNext, TResult, TRootSelectTRow, TProjection, TNext, TMiddle, TResult, TRootWhereSelectTRow, TPredicate, TProjection, TNext, TMiddle, TResult, TRootStopTResult, TRoot每个节点都实现了同一个接口Copyinternal interface IQueryNodeTRow, TResult, TRoot{static abstract void Run(ReadOnlySpan rows, scoped ref QueryRuntime runtime);static abstract void Process(in TRow row, scoped ref QueryRuntimeTResult runtime);}这里可以简单理解成Run 是外面那一圈大循环整体遍历Process 是对单行执行的逻辑。比如 Where 节点大概长这样Copyinternal readonly struct WhereTRow, TPredicate, TNext, TResult, TRoot: IQueryNodeTRow, TResult, TRootwhere TPredicate : IFilterwhere TNext : IQueryNodeTRow, TResult, TRoot{public static void Run(ReadOnlySpan rows, scoped ref QueryRuntime runtime){for (var i 0; i rows.Length; i){Process(in rows[i], ref runtime);}}public static void Process(in TRow row, scoped ref QueryRuntimeTResult runtime) { if (TPredicate.Evaluate(in row)) { TNext.Process(in row, ref runtime); } }}关键点在于管道的形状完全藏在这些类型参数里面每个节点是一个只有静态方法的 struct —— 不需要创建实例没有虚调用。对 JIT 来说一旦这些泛型类型参数都被代入这就是一张普通的静态调用图而已。列和投影#查询总得运行在某种行类型 TRow 上这通常是你自己定义的一个 record/class/struct。每一列会实现这样一个接口Copyinternal interface IColumnTRow, TValue{static abstract string Identifier { get; }static abstract TValue Get(in TRow row);}举个简单的例子Copyinternal readonly struct PersonNameColumn : IColumnPerson, string{public static string Identifier “Name”;public static string Get(in Person row) row.Name;}而投影SELECT 后面那部分则实现Copyinternal interface IProjectionTRow, TResult{static abstract TResult Project(in TRow row);}将选出某一列本身做成一个投影可以这么写Copyinternal readonly struct ColumnProjectionTColumn, TRow, TValue: IProjectionTRow, TValuewhere TColumn : IColumnTRow, TValue{public static TValue Project(in TRow row) TColumn.Get(row);}多列选择时TypedSql 会构造专门的投影把结果拼成 ValueTupleCopyinternal readonly struct ValueTupleProjectionTRow, TColumn1, TValue1: IProjectionTRow, ValueTuplewhere TColumn1 : IColumnTRow, TValue1{public static ValueTuple Project(in TRow row) new(TColumn1.Get(row));}// … 一直到 7 列然后通过一个“Rest”再递归挂一个 IProjection还是同样的模式全是 struct全是静态方法。过滤器#过滤器的接口长这样Copyinternal interface IFilter{static abstract bool Evaluate(in TRow row);}一个最常用的比较过滤器形式是列 字面量Copyinternal readonly struct EqualsFilterTRow, TColumn, TLiteral, TValue : IFilterwhere TColumn : IColumnTRow, TValuewhere TLiteral : ILiteralwhere TValue : IEquatable, IComparable{[MethodImpl(MethodImplOptions.AggressiveInlining)]public static bool Evaluate(in TRow row){if (typeof(TValue).IsValueType){return TColumn.Get(row).Equals(TLiteral.Value);}else{var left TColumn.Get(row);var right TLiteral.Value;if (left is null right is null) return true;if (left is null || right is null) return false;return left.Equals(right);}}}这里我们通过判断 TValue 是值类型还是引用类型来分别处理 null 的情况。.NET 的 JIT 能够识别这种模式并且为值类型和引用类型分别特化并生成不同的代码路径从而实际上并不存在任何的分支开销。GreaterThanFilter、LessThanFilter、GreaterOrEqualFilter、LessOrEqualFilter、NotEqualFilter 等等都是同样的套路。逻辑运算也是在类型层面组合的Copyinternal readonly struct AndFilterTRow, TLeft, TRight : IFilterwhere TLeft : IFilterwhere TRight : IFilter{public static bool Evaluate(in TRow row) TLeft.Evaluate(in row) TRight.Evaluate(in row);}internal readonly struct OrFilterTRow, TLeft, TRight : IFilterwhere TLeft : IFilterwhere TRight : IFilter{public static bool Evaluate(in TRow row) TLeft.Evaluate(in row) || TRight.Evaluate(in row);}internal readonly struct NotFilterTRow, TPredicate : IFilterwhere TPredicate : IFilter{public static bool Evaluate(in TRow row) !TPredicate.Evaluate(in row);}所以一条 WHERE 子句最终就会变成一棵泛型过滤器类型树每个节点只有一个静态 Evaluate 方法。值类型特化版字符串ValueString#在 .NET 里string 是一个引用类型这给 TypedSql 带来了一些麻烦.NET 会对引用类型采用共享泛型在运行时做分发而不是为 string 泛型实例化一个具体类型这使得运行时会产生类型字典查找的开销。虽然这点开销不大但是 TypedSql 追求的是媲美手写循环的性能所以我想尽量把热路径里涉及的类型都做成值类型。于是我选择把字符串包在一个小的值类型里Copyinternal readonly struct ValueString(string? value) : IEquatable, IComparable{public readonly string? Value value;public int CompareTo(ValueString other) string.Compare(Value, other.Value, StringComparison.Ordinal); public bool Equals(ValueString other) { return string.Equals(Value, other.Value, StringComparison.Ordinal); } public override string? ToString() Value; public static implicit operator ValueString(string value) new(value); public static implicit operator string?(ValueString value) value.Value;}再配一个适配器把原来的 string 列变成 ValueString 列Copyinternal readonly struct ValueStringColumnTColumn, TRow: IColumnTRow, ValueStringwhere TColumn : IColumnTRow, string{public static string Identifier TColumn.Identifier;public static ValueString Get(in TRow row) new(TColumn.Get(in row));}在内部所有字符串列都统一成 ValueString有几个好处热路径里尽量是值类型少一点引用类型的干扰避开了泛型共享带来的类型字典查找开销。对使用者来说你照样写 string而我的 TypedSql 会在内部自动在边缘位置做封装/解封装所以完全透明。实现一个 SQL 子集#TypedSql 并不打算做成一个大而全的 SQL 引擎而是针对单表、内存内查询设计了一个很小的 SQL 方言支持这些语句SELECT * FROM $SELECT col FROM $SELECT col1, col2, … FROM $WHERE 支持比较, !, , , , 布尔AND, OR, NOT括号字面量支持整数如 42浮点数如 123.45布尔true / false单引号字符串‘Seattle’内部用 ‘’ 转义null列名大小写不敏感$ 代表当前行来源整体解析流程很简单先把 SQL 字符串切成 token再构建一棵小 AST包含ParsedQuery整体查询SelectionSelectAll 或者列名列表WhereExpression筛选表达式ComparisonExpression比较AndExpression与OrExpression或NotExpression非LiteralValue字面量LiteralKind.Integer IntValueLiteralKind.Float FloatValueLiteralKind.Boolean BoolValueLiteralKind.String StringValuestring?LiteralKind.Null在这个阶段整个系统其实完全不知道 C# 里面的类型是什么样的列又是什么只是单纯看作 SQL 结构。类型检查、以及这个字面量能不能用在那一列上之类的问题会留到后面的编译阶段去做。把字面量变成类型 —— 包括字符串#在这里我想针对每一个 SQL 语句都生成一份独特的类型因此作为查询条件中的字面量也必须变成类型参数的一部分。于是在 TypeSql 中所有的字面量类型都实现同一个接口Copyinternal interface ILiteral{static abstract T Value { get; }}适用范围包括整数int浮点数float字符char布尔bool字符串这里是 ValueString内部包 string?……未来还可以扩展更多数值字面量#数值字面量的编码方式很直接用 16 进制和位运算拼出来。先来一组 IHex 接口和 Hex0–HexF structCopyinternal interface IHex { static abstract int Value { get; } }internal readonly struct Hex0 : IHex { public static int Value 0; }// …internal readonly struct HexF : IHex { public static int Value 15; }然后一个整型字面量长这样Copyinternal readonly struct IntH7, H6, H5, H4, H3, H2, H1, H0 : ILiteralwhere H7 : IHex// …where H0 : IHex{public static int Value (H7.Value 28)| (H6.Value 24)| (H5.Value 20)| (H4.Value 16)| (H3.Value 12)| (H2.Value 8)| (H1.Value 4)| H0.Value;}浮点数也是一样的 8 个十六进制数位只不过最后用 Unsafe.BitCastint, float 转回 floatCopyinternal readonly struct FloatH7, H6, H5, H4, H3, H2, H1, H0 : ILiteralwhere H7 : IHex// …{public static float Value Unsafe.BitCastint, float((H7.Value 28)| (H6.Value 24)| (H5.Value 20)| (H4.Value 16)| (H3.Value 12)| (H2.Value 8)| (H1.Value 4)| H0.Value);}字符则是 4 个十六进制数位Copyinternal readonly struct CharH3, H2, H1, H0 : ILiteralwhere H3 : IHex// …{public static char Value (char)((H3.Value 12)| (H2.Value 8)| (H1.Value 4)| H0.Value);}

相关新闻

程序员转型大模型:技术栈升级与职业发展指南

程序员转型大模型:技术栈升级与职业发展指南

1. 程序员转型大模型的核心价值解析程序员转型大模型领域绝非简单的技术栈切换,而是职业发展路径的战略升级。从2023年开始,全球科技巨头和初创企业在大模型领域的投入呈现指数级增长。根据LinkedIn最新职业报告显示,大模型相关岗位的年增长率…

2026/7/23 10:53:14 阅读更多 →
MSPM0 PMCU低功耗架构解析:从电源管理到时钟策略的嵌入式实战

MSPM0 PMCU低功耗架构解析:从电源管理到时钟策略的嵌入式实战

1. 项目概述与核心价值 在嵌入式开发,尤其是电池供电的物联网终端、可穿戴设备或便携式仪器领域,我们每天都在和两个“看不见的敌人”作斗争:一个是有限的电池容量,另一个是随时可能到来的实时任务。如何让设备在99%的时间里“深度…

2026/7/23 10:52:14 阅读更多 →
TypeScript超越Python:静态类型在前端工程中的崛起

TypeScript超越Python:静态类型在前端工程中的崛起

1. TypeScript登顶Github语言榜首的背后逻辑 上周Github发布的年度Octoverse报告显示,TypeScript首次超越Python成为平台最受欢迎编程语言。这个结果既在意料之外又在情理之中——作为JavaScript的超集,TypeScript通过静态类型系统解决了前端开发的诸多痛…

2026/7/23 10:52:14 阅读更多 →

最新新闻

10f7转换包:高效实现12种数据格式互转的解决方案

10f7转换包:高效实现12种数据格式互转的解决方案

1. 项目背景与需求解析 10f7转换包是近期在开发者社区中频繁出现的一个工具包,主要用于解决不同数据格式之间的转换问题。作为一名长期处理数据对接的开发人员,我最近在实际项目中遇到了几个典型场景: 需要将老旧的CSV报表转换为JSON格式供新…

2026/7/23 11:17:24 阅读更多 →
Cola AI编程助手:July限免版实战指南与开发效率提升

Cola AI编程助手:July限免版实战指南与开发效率提升

如果你最近在关注 AI 编程助手领域,可能会发现一个现象:新模型层出不穷,但真正能在实际开发中显著提升效率的却不多。很多工具要么是"玩具级"的简单代码补全,要么是"专家级"的高门槛产品,很少有能…

2026/7/23 11:17:24 阅读更多 →
AI模型选型决策树:从57个开源模型中精准筛选出最适合你团队的3款,附实测性能对比表(2024Q2最新)

AI模型选型决策树:从57个开源模型中精准筛选出最适合你团队的3款,附实测性能对比表(2024Q2最新)

更多请点击: https://codechina.net 第一章:AI模型选型决策树:从57个开源模型中精准筛选出最适合你团队的3款,附实测性能对比表(2024Q2最新) 面对2024年第二季度激增的57个主流开源大语言模型(…

2026/7/23 11:17:24 阅读更多 →
链式思考(CoT)在智能编程中的实践与优化

链式思考(CoT)在智能编程中的实践与优化

1. 链式思考(CoT)在Claude Code中的核心价值在Claude Code这类智能编程助手中,链式思考(Chain-of-Thought,简称CoT)提示技术正在改变开发者与AI的协作方式。不同于传统单轮问答,CoT通过显式要求…

2026/7/23 11:17:24 阅读更多 →
学术论文AI生成痕迹检测与自查方法详解

学术论文AI生成痕迹检测与自查方法详解

1. 项目背景与核心需求去年帮导师审稿时遇到一篇"完美"论文,结构工整、表述流畅,但总觉得哪里不对劲。后来用专业工具检测才发现,全文62%的内容存在AI生成特征。这件事让我意识到:在学术写作中,提前识别AI生…

2026/7/23 11:17:24 阅读更多 →
智能音视频前端:AI 辅助的字幕生成与内容审核界面

智能音视频前端:AI 辅助的字幕生成与内容审核界面

智能音视频前端&#xff1a;AI 辅助的字幕生成与内容审核界面 一、音视频前端的 AI 集成挑战&#xff1a;Web Worker 资源分配与实时性平衡 音视频前端是浏览器中资源消耗最高的场景之一。一个典型的视频编辑页面需要同时处理&#xff1a;<video> 元素的播放控制、Canvas…

2026/7/23 11:16:24 阅读更多 →

日新闻

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;从单点好评到指数级传播&#xff1a;AI副业主理人必须掌握的4层口碑渗透模型&#xff08;含ROI测算表&#xff09; 当AI副业主理人不再仅满足于单次服务交付&#xff0c;而是主动构建可复用、可裂变、可…

2026/7/23 0:00:25 阅读更多 →
AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;AI写作开头钩子设计&#xff1a;为什么你的AI文案完读率不足18%&#xff1f;——基于2,346篇A/B测试报告的归因分析 在对2,346篇跨行业AI生成文案的A/B测试数据进行聚类分析后&#xff0c;我们发现&#xff1…

2026/7/23 0:01:26 阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南&#xff1a;免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:01:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中&#xff0c;我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源&#xff0c;还是配置文件、证书等&#xff0c;都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下&#xff0c;但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP&#xff08;轻量级目录访问协议&#xff09;作为企业级身份认证的黄金标准&#xff0c;已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时&#xff0c;发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”&#xff0c;而是以可解释、可审计、可迭代的方式&#xff0c;赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻