三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

C#手写数据类和protoc自动生成类的转换

C#手写数据类和protoc自动生成类的转换

背景

protobuf作为著名的二进制序列化方案,却不能直接把自己写的数据类序列化,只能使用protoc生成类。对于上百个字段,有嵌套类、列表、字典的玩家数据,如果项目已经大量使用自己写的数据类,手写转换方案是一个灾难。

问题抽象

我们需要在一个手写数据类和一个pb数据类之间建立联系,实现手写>pb、pb>手写的转换方法。

基于泛型接口

如果弄一个泛型接口IToPB<T>,T给pb类,所有要转换的手写类继承这个接口,里面有两个转换方法……我们发现里面可以放到PB类的方法,PB类到手写类的方法需要输入T,返回它的具体子类,这里没法写具体子类,而且没法写成实例方法,它需要new一个实例。那么我们不返回手写类的实例了,我们new一个手写对象,让它执行ToMy(T t),把里面的字段赋值。

public interface IToPB<T>{ public T ToPB(); public void ToMy(T t); }

这样并没有减少代码量,只是把转换代码分散到各数据类里。

对于一个大型嵌套数据类,调用ToPB,里面是各字段的ToPB方法,像金字塔一样从简单到复杂垒起来。

基于特性+反射

需要一个类的特性ToPB,参数是它的PB类。一个字段的特性PBField,参数是它的PB类对应字段的名字。反射先看它有没有ToPB,提取类型参数,创建PB实例,再拆解字段,看有没有PBField特性,有则提取字段名,去PB类里找字段,赋值,处理类型不匹配报错。

PB字段名改名后,特性的参数也要改。还没有语法报错,需要我们自己记得。

要加大量特性、特性参数,要写基于反射的转换方法,代码量也不小,比泛型接口还更绕了。

基于自动生成代码

我们可以由手写的C#类自动生成对应的proto脚本,并在C#类里添加和protoc生成类的互相转换方法。

总结

我们不可能全自动的,用很少劳动量实现一个转换方案。

已经写好了C#数据类>
要写互相转换方法,所有数据类都要写>
懒得都写,关卡数据部分直接用proto的,proto数据类比C#多了关卡数据>
C#转proto时直接new,之前的关卡数据丢失>
要么C#数据类一个都不少,要么写一个proto传入的转换方法,破坏接口

扩展:任意大量A型类需要和B型类相互转换

类似的情景还有场景保存,把场景内大量不同的类转换成存档数据类,读档时要把数据类还原成物体,不是简单的new,而是找到预制体,实例化,然后写入字段。

然后就不能先new然后void Load(data)填充字段,需要加载方法把生成物体也包含,那么不能是实例方法。可以是静态方法,但是就不能用接口约束。如果把加载方法写在数据类,那如果是个protobuf生成类,最好不要改写,而是使用扩展方法。

最终方案就2个

  1. 物体类使用静态方法,放弃用接口约束;
  2. 给protobuf存档类加扩展方法。如果将来protobuf类改名了,扩展方法也要改一下;

综上,方案1好一点。

← 返回列表