简介
Natasha 是一个基于 Roslyn 的动态编译类库,它以极简的 API 完成了动态编译的大部分功能,使用它可以在程序运行时编译出新的程序集。
Natasha 允许开发人员直接使用 C# 代码即可编写运行时的功能,避免了 Emit 的学习、开发、维护的成本。
Natasha 的编译单元的基本输出是程序集,程序集加载在域中,使用该类库可以创建域,并在不同的域内创建新的功能,也支持可以卸载域。
Natasha 为开发人员提供了插件管理的功能,并内置解析依赖的功能,我们可以有针对性的加载和卸载插件及其依赖。
使用 Natasha 我们可以快速的实现映射/类库粘合/业务逻辑组装/动态代理/协议转换等等,使用 Natasha 需要一定的编程基础,有动态编译经验相关的实践。尽管 Natasha 可以输出详细日志,但是动态功能的开发本应该是逻辑畅通,代码规范的,如果你不能保证代码是畅通的,且没有动态编译的思维可以先找相关的文章进行学习。
项目链接地址: https://github.com/dotnetcore/Natasha
Natasha 的进化
Natasha 自 v3.0.0 版本之后,进行了比较大的革新,以下我列举一些比较重要的:
移除对 .NET Standard2.0 / .NET Core 2.1-3.0 的支持,目前支持的版本为:.NET Core 3.1/.NET 5.0/.NET 6.0,兼容版本已归档至其他分支。Runtime 是 Natasha 的强依赖, .NET 从 3.0 起,Runtime 升级了很多新的特性, 比如支持可空引用及元数据处理,支持 ALC 高级特性,方便的插件依赖解析方案等等,另外由于个人精力有限,最终决定从分水岭 3.1 开始做兼容。
语义过滤,自 v3.0.0 以后,Natasha 新增了语义层,这让 Natasha 显得更加智能,我们可以根据自己的需求去解析和重组语法语义。比如 Natasha 内置的语义处理,其中有一个功能是将无用的 using 移除掉,只保留有用的 using。大大减少了日志输出的代码,让开发者一眼看到有用的代码逻辑。编译一段代码,需要引用元数据,需要准备 using,需要写正确的代码,最后是输出方式,为了让开发者以最快速度上手,Natasha 解决了元数据引用问题,以及 using 覆盖问题,另对外提供了输出 API,理论上开发者需要关注的是自己如何编写一段动态代码, 如何与运行时其他功能接洽配合。
另针对命名空间滥用导致的多义性引用问题,我们提供了语义扩展包
Natasha.CSharp.Extension.Ambiguity
性能优化,优化是 Natasha 一直在做的事情,Natasha 在预热方法上进行了一些并发的改造,以便让相互无关的任务并行初始化,除此之外也移除了 AdWorkspace 代码,这些代码均由高性能的方案取代。针对性能敏感场景, 我们增加了预热时全局修剪错误语义,以及禁用语义过滤的 API,以便让编译更快的发生。
新特性支持。支持 C# 最高语言版本,支持可空引用的编译(默认关闭),DLL/PDB/XML 文件手动选择生成。Natasha 4.0 的源码使用了可空引用支持,因此支持可空引用项目的接入和使用,在方法调用的上下游明确了可空界限。但在动态编译层面,尝试可空引用的元数据解析功能时,我遇到了无法解决的难题,可空引用的顶层泛型传递是无法正确解析的,因此在
Action<string?>
等元数据解析中,无法得到正确结果,另外方法返回值可空引用解析也未找到方法,综上 Natasha 默认关闭了动态编译初始化中的可空引用的选项,开发者需要自行开启。重构引用管理,4.0 重构了 Natasha 的引用管理,经过探索我们最终选定以
AssemblyName
作为引用版本管理的依据,并在预热过程中采用只读上下文解析引用。重构插件管理,4.0 重构了 Natasha.Domain 域实现,摘除了对引用管理的耦合,让
NatashaDomain
更专注于程序集及其依赖的加载与卸载,此次对外提供了4个方法以便于用户在加载插件时管理不同版本的依赖。重构日志模块,4.0 版本后日志将由用户自行控制写入和获取,对外提供编译后的日志处理事件,Natasha 将不再负责日志的 IO 部分。
重构异常模块,4.0 重构了 Natasha 的编译单元,其中语法转换阶段如果出错会抛出异常,之后编译不成功也会抛出异常,并提供编译成功和失败事件。
实战应用
Natasha 已经在网友公司及我司得到了广泛应用,比如:客户端脚本定制,动态计分系统,低代码应用中的逻辑组装与编译, 类库中字符串到表达式树的转换,基于算法的高性能只读并发字典,忽略类库版本的代码粘合剂,实体映射,动态路由,动态 RPC, 动态定时任务等等。
Natasha 与其他技术
SG(Source Generetor)
首先来讲 Natasha 与 SG 有重叠的部分,但也有各自取代不了的地方。我认为比较好的组合是,SG 提供静态约束,Natasha 来提供动态实现。
AOT
Natasha 是尽可能覆盖全面的程序集和引用,剪裁需谨慎,另外 AOT 不会取代动态编译,只会平行发展。
未来规划
Natasha 完成了自己的核心任务,但它还有很长一段的路要走,期待大家的反馈。
后续 Natasha 还有一些方案需要调研,一些应用需要开发,例如:域的强制卸载方案,基于 ASP.NET 的动态开发框架,高度灵活的高性能实体映射库等。
下一篇将介绍 Natasha 的域组件与插件编程。