这本来是个早就可以写的分享。因为代码几周前就迁移并准备好了。而且这也是之前项目的工具,因为可以抽离出来通用化所以单独整理出来。

这个项目起源于我们之前哪个项目,客户端想要在Unity的C#里动态加载配置,而protobuf-net一方面大量使用反射而性能不太行,另一方面使用的时候得生成C#代码才行。客户端原来的做法是把消息扁平化了,使用protobuf-net得底层读写接口直接操作基本数据类型。这就失去了结构化带来的一系列好处。再加上后来我引入了跨平台导表工具,使用结构化得数据会非常方便,而手动把这个数据打散到客户端读取接口显然很浪费人力而且容易出错。所以我就干脆也使用protobuf-net的底层读写接口做了现在的DynamicMessage的支持,API设计是结合pbc和protobuf官方的API流程的。

其实倒是可以剥离对protobuf-net的支持,因为我用到的底层接口基本上只有wiretype的读写那部分,这部分其实比较容易,无非处理下zigzag编码和varint。这两个我之前也都手写过,其中zigzag编码的原理用于实现以前内存混淆的整数,而varint用于libatbus的流通道传输时一个message开始用于表示整个message长度的header(详见: https://github.com/atframework/libatbus/blob/master/include/detail/buffer.h)。唯一一个比较麻烦的地方是,protobuf打包message的时候是先存长度,再存二进制内容的。那么一开始计算长度的时候就得递归进去计算出所有嵌套结构得长度,这比较麻烦,我也懒得搞,所以就还是用protobuf-net来做这一部分了。

整个结构就是有一个Factory,可以读取pb文件,建立message和enum类型索引。然后根据message得类型来创建、设置、添加和移除field得内容。当时我们没有用到所以没做对pack=true得支持。不过加起来也不困难。另外希望保证稳定性,所以全程没有exception,都是返回错误码然后提供Error Message。Message是会关联到所绑定的Factory的,共享同一份message和enum类型的索引。所以也可以用Message直接创建另一个Message。

最初这个功能就是为了读表使用的。所以写好的工程就放在了xresloader的分组里,而且sample的文件、协议和代码也直接用了xresloader的sample。项目在 https://github.com/xresloader/DynamicMessage-net 里面的ExcelConfig目录是用于xresloader的读表和建立索引的,protobuf-net直接就是原本的protobuf-net。前者不需要可以不要,后者可以直接用其他项目里已有的。

工程配置成了.net standard,在.net framework和.net core里都有单独的sample并且都可以用。