给 SDK 开发者的建议

如果你对现有的 OneBot C# SDK 都不满意,并打算自己再造一个轮子,为了避免重复陷入弯路,请认真考虑以下建议。

当然,我的水平只能给刚开始实践 C# 编程的新晋开发者提供一些建议。对于有经验的大佬,我说的很可能是正确的废话,甚至部分内容干脆是片面的。恳请各位大佬斧正。

想清楚定位

目前 OneBot SDK 就应有两种不同的定位。用 .NET 官方的项目进行类比,一种是类似于 HttpListener在新窗口打开 的简单访问库,这也是 WudiLib 的定位;另一种是类似于 ASP.NET Core在新窗口打开 这种包含完整的命令处理的大框架,我也有这种框架在新窗口打开,但我觉得目前的质量还不值得拿出来给大家使用。前者应尽可能简单,后者可以依赖前者。如果搞不清楚自己的定位,就会出现过度或不伦不类的设计。

对于简单的 SDK,主要任务是处理请求、反序列化等,将 bot 开发者从繁杂的重复工作中解放出来。例如 公开 API在新窗口打开 中每一个 API 都有不同的参数和返回类型,SDK 应该定义好所对应的请求方法和返回类型,这样 bot 开发者就可以快速开发出包含简单功能的 bot,并享受 C# 强静态类型带来的便利。

对于功能完整的框架,除了上面简单 SDK 的任务,还应该包含指令路由、日志、生命周期管理等功能。这样就可以提高 bot 应用的稳定性和可维护性。

除此之外,NoneBot2在新窗口打开 等框架还带插件商店,可以运行其他人写的插件,有点儿接近以前的酷 Q 了。我没有发布插件让别人运行的经验,无权对此指点江山。因此本页面主要讨论 bot 开发者和运营者是同一人,你的 SDK 或框架为此类人服务的情况。

先积攒一定的经验

我强烈建议所有人不要冒然开始开发 SDK 或框架,而是先做一个包含一定功能的 bot。尤其是目前绝大多数 SDK/框架开发者还只是学生(我也是)的情况下。SDK 可能还好,因为比较简单;框架则会牵扯到更多东西,至少要有几个不同的数据来源,经历几次需求变更(例如本来依赖的服务变得不可用),才能更好地理解 bot 开发者对框架的需求。

充分参考现有项目,吸收现有项目的优点

无论你要开发 SDK 还是框架,我都建议你浏览现有的几个 C# OneBot SDK/框架,甚至也应该参考其他一些语言的框架。考虑这么设计或实现的优点和缺点是什么,取长补短,避免走其他人走过的弯路。

简单的 SDK

避免过度设计

如果你要设计一个用来收发消息和事件的简单 SDK,请保持设计简单。这种 SDK 只需要收、发、API 列表、事件解析和分发各一个基类或接口(根据需要创建子类),并定义一些事件和 API 响应的类即可。

注重可扩展性

OneBot 协议只规定了基础的 API、事件列表和三(或四)种通信方式,其中每项都可以进行扩展。bot 开发者使用和 SDK 开发者不同的 OneBot 实现是完全可能的。如果 bot 开发者想访问某个 API,而这个 API 在 OneBot 标准和其他实现中都没有,他会希望 SDK 开发者预留了相应的方式使其访问此 API。同理,bot 开发者可能用不同的通信方式和事件列表,SDK 应预留对应的可扩展性。

控制依赖

SDK 只负责最基本的通信,通常不需要依赖过多外部的库,同时也不常用到 .NET 新版本的新特性。因此,我建议 SDK 应依赖尽可能少的外部库,并尽可能降低最低要求的 .NET 版本。

开启 nullable reference types

可为空引用类型在新窗口打开 是 C# 8.0 引入的新特性,建议启用,增强健壮性。

完整框架

DI 是绝对必要的

当 bot 功能与个人信息强关联,并且需要从多个数据源更新数据时,DI 可以大大提升可维护性,减少屎山。这是我开发 bot 四年最直接的体会,DI 是绝对必要的。

保留一定的灵活性

例如,我建议把事件的各属性设计成可修改的,并且允许任意构造或进行其他处理,以方便可能有需求的中间件。

提示

这种灵活性还使适配其他协议(如钉钉、Discord)成为可能,但是……说到这儿好像有点儿多了。

考虑 IDE 集成

在这种框架下开发机器人,往往需要用很多特性(Attribute)在新窗口打开控制指令触发等。就像 VS 可以直接在 ASP.NET Core 项目中添加控制器并自动生成对应视图,框架开发者可以提供能够在 VS 等 IDE 中使用的模板,帮助 bot 开发者减少编写重复代码,快速创建项目或添加新功能等。

泛泛的建议

这些建议不仅仅是针对开发 SDK 或框架,而是给新晋开发者在编程中的建议。

多读文档,正确地运用 C# 语言特性

以异步编程为例,如果一个方法不打算返回任何东西,并且它是异步的,那么它的返回值类型可以为 voidTaskValueTask 暂且不论)。那么应该返回什么呢?官方文档 异步返回类型 (C#)在新窗口打开 就说明了只有此方法作为事件处理器,并且这个事件处理器要求返回 void 时,异步方法才应该用 void 作为返回值,其余情况都应该用 Task 作为返回值。而且,返回 void 的异步方法不应引发异常,如果引发,则会导致程序故障。

私货(不是)

WudiLib 使用 C# 的事件机制处理 OneBot 中的事件,并且要求事件处理器返回 void 类型,这个设计并不好。想一想,为什么?点此查看所提及的源码在新窗口打开

消息的事件处理器常常是异步的,因为指令触发后常常需要异步地访问外部资源(如读取数据库等)。这种设计会使部分 bot 开发者无意识地编写返回 void 类型的异步方法。如果他们没有在方法内捕获并处理异常,就可能导致整个应用程序崩溃。事实上,返回 void 的异步方法几乎只在 GUI 程序中才会用到。

提示

你能找出 ValueTask 的官方文档,阅读并总结出 ValueTask 的使用要点吗?

这部分文档翻译得不好,建议找到后阅读英文原文。

多阅读优秀项目源码

我还建议每个开发者多阅读像 .NET runtime在新窗口打开 这样的优秀项目的源码,学习其中的思路和思想。

总结

SDK 和框架的设计有相当大的自由发挥的空间,我在此只提出几点我认为无论如何设计,都应该认真考虑的建议。这是我针对在阅读其他 SDK 代码、文档,及与其开发者交流中发现的一部分常见问题提出的建议。这些建议基于我的个人经验,难免会有疏漏,再次恳请各位大佬不吝斧正。

关于我对理想的 SDK 和框架的想法,会在蓝图中更新。