尝试使用 protobuf-net 序列化 System.Numerics.Quaternion

Posted

技术标签:

【中文标题】尝试使用 protobuf-net 序列化 System.Numerics.Quaternion【英文标题】:Trying to serialize System.Numerics.Quaternion using protobuf-net 【发布时间】:2021-02-11 07:18:15 【问题描述】:

背景资料

我有一个 .Net Standard 2.0 类库,它使用 Protobuf-net.grpc 的代码优先方法来定义 gRPC 服务。在这个定义中,我有定义不同数据结构的类,我们使用这些数据结构来记录传感器数据并使用 protobuf-net 将其序列化。我的程序正在摄取数十万个大对象/秒(很快将扩展到数百万个),旨在用于嵌入式环境。

问题

在下面的课程中,我想将 System.Numerics.Quaterion 作为成员包括在内。我似乎无法将其序列化。使用静态构造函数时,RuntimeTypeModel 会抛出异常,因为在执行静态构造函数时已经以某种方式创建了四元数模型。由于这是一个类库,我非常希望避免在每个使用 gRPC 服务的不同程序中调用 RuntimeTypeModel。我希望找到一种方法来序列化 System.Numerics.Quaternion。

我尝试将此静态构造函数运行时定义放在类层次结构的***别,但无济于事。仍然抛出异常。

[ProtoContract]
    public class IMUData : SensorData, ISensorData
    
        static IMUData()
        
            RuntimeTypeModel.Default.Add(typeof(Quaternion), false)
                .Add("W")
                .Add("X")
                .Add("Y")
                .Add("Z");
        

        ... //Other members

        [ProtoMember(8)]
        public Quaternion Orientation
        
            get; set;
        

        ... //Other methods and members
    

问题

我想做的事情是否可能,或者我应该简单地创建自己的四元数类并定义隐式运算符? (我宁愿避免这种情况,因为处理数十亿个这样的对象需要足够长的时间)

【问题讨论】:

顺便说一句:IMO,这里的正确顺序是 XYZW - 由 Quaternion(float x, float y, float z, float w) 构造函数定义 - 所以如果这是我, 我会订购它们 X=1,Y=2,Z=3,W=4;我之所以这么说是因为这种类型实际上非常接近库会自动识别和使用的内置“类元组类型”模式 - 如果一个类型(没有合适的注释)有一个构造函数匹配所有成员(除了参数名称上的大小写),它会推断它的意思是“构造函数参数顺序定义了字段编号” 【参考方案1】:

这归根结底是一个时序问题——当序列化器尝试反射SensorData准备序列化器时,IMUData中的静态构造函数还没有执行,所以它准备了序列化器信息不完整,然后稍后静态构造函数尝试重新配置模型 - 为时已晚。

如果您使用 C# 9.0,则可以通过使用模块初始化程序而不是静态构造函数来解决此问题(如果我们假设 SensorDataIMUData 在同一个模块中,可能 一个安全的假设)。以下工作正常,例如:

[ProtoContract]
public class IMUData : SensorData //, ISensorData

    [ModuleInitializer]
    internal static void Init()
    
        RuntimeTypeModel.Default.Add(typeof(Quaternion), false)
            .Add("W")
            .Add("X")
            .Add("Y")
            .Add("Z");
    

请注意,如果您不使用 .NET 5(当前为预览版),您可以自己定义必要的属性:

namespace System.Runtime.CompilerServices

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    internal sealed class ModuleInitializerAttribute : Attribute  


如果这不是一个选项,您可以简单地将模型配置代码更早地放在您的应用程序中 - 最好是在启动期间,以便它发生在 long 之前序列化程序尝试开始构建模型。

【讨论】:

谢谢马克!很抱歉我没有遇到模块初始化程序。感谢您抽出宝贵时间回答与 protobuf-net 没有直接关系的问题! @craig 它们是 c# 9 中的新功能,尚未发布 iirc,但如果您有 .net 5 sdk,则可以使用它

以上是关于尝试使用 protobuf-net 序列化 System.Numerics.Quaternion的主要内容,如果未能解决你的问题,请参考以下文章

使用 protobuf-net 反序列化 int& 类型

使用 Protobuf-Net 序列化未知子类型

序列化动态类型参数 Protobuf-net

用 Protobuf-net 替换 binaryformatter

Protobuf-net - 啥被序列化了?

使用 protobuf-net 序列化 List<IComparable> 成员