首页
/ MessagePack-CSharp在Unity IL2CPP环境下处理自定义类型序列化的解决方案

MessagePack-CSharp在Unity IL2CPP环境下处理自定义类型序列化的解决方案

2025-06-04 09:56:39作者:翟江哲Frasier

背景介绍

在使用MessagePack-CSharp进行Unity3D开发时,开发者可能会遇到一个常见问题:当项目使用IL2CPP编译后端时,包含自定义类型的对象数组无法正常序列化。这个问题在Mono环境下可能不会出现,但在IL2CPP环境下会抛出"Not supported primitive object resolver"异常。

问题分析

问题的核心在于MessagePack的PrimitiveObjectFormatter对于非基本类型(如int、bool、string等)的处理机制。在IL2CPP环境下,当尝试序列化一个包含自定义类型(如AirSimYawMode、AirSimPose等)的对象数组时,系统无法自动识别这些自定义类型,导致序列化失败。

解决方案

自定义解析器实现

要解决这个问题,我们需要创建一个自定义的解析器(CustomObjectArrayFormatter),专门用于处理对象数组的序列化和反序列化。这个解析器需要:

  1. 识别数组中的各种数据类型
  2. 为每种类型提供专门的序列化/反序列化逻辑
  3. 处理基本类型和自定义类型的不同情况
public class CustomObjectArrayFormatter : IMessagePackFormatter<object[]>
{
    public void Serialize(ref MessagePackWriter writer, object[] value, MessagePackSerializerOptions options)
    {
        writer.WriteArrayHeader(value.Length);
        foreach (var item in value)
        {
            switch (item)
            {
                case int intValue:
                    writer.Write(intValue);
                    break;
                case float floatValue:
                    writer.Write(floatValue);
                    break;
                case bool boolValue:
                    writer.Write(boolValue);
                    break;
                case string stringValue:
                    writer.Write(stringValue);
                    break;
                case AirSimYawMode yawMode:
                    options.Resolver.GetFormatterWithVerify<AirSimYawMode>().Serialize(ref writer, yawMode, options);
                    break;
                // 其他自定义类型处理...
                default:
                    throw new InvalidOperationException("Unsupported type encountered: " + item.GetType());
            }
        }
    }

    public object[] Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
    {
        var len = reader.ReadArrayHeader();
        var result = new object[len];
        for (int i = 0; i < len; i++)
        {
            var type = reader.NextMessagePackType;
            switch (type)
            {
                case MessagePackType.Integer:
                    result[i] = reader.ReadInt32();
                    break;
                case MessagePackType.Float:
                    result[i] = reader.ReadSingle();
                    break;
                case MessagePackType.Boolean:
                    result[i] = reader.ReadBoolean();
                    break;
                case MessagePackType.String:
                    result[i] = reader.ReadString();
                    break;
                default:
                    throw new InvalidOperationException("Unsupported MessagePackType encountered: " + type);
            }
        }
        return result;
    }
}

解析器注册

创建自定义解析器后,需要将其注册到MessagePack的解析器系统中。这通常在应用程序启动时完成:

var customize = CompositeResolver.Create(
    new IMessagePackFormatter[] { new CustomObjectArrayFormatter() },
    new[] { StandardResolver.Instance }
);

StaticCompositeResolver.Instance.Register(
    customize,
    MessagePack.Resolvers.StandardResolver.Instance,
    MessagePack.Resolvers.GeneratedResolver.Instance
);

实现细节说明

  1. 类型识别:在序列化过程中,使用C#的模式匹配功能(switch-case)来识别不同类型的值,并为每种类型提供专门的序列化逻辑。

  2. 自定义类型处理:对于自定义类型(如AirSimYawMode),通过options.Resolver获取对应的格式化器进行序列化。

  3. 数组处理:在序列化和反序列化时,首先处理数组头部信息(数组长度),然后逐个处理数组元素。

  4. 错误处理:遇到不支持的类型时抛出明确的异常信息,便于调试。

性能考虑

  1. 模式匹配优化:C#的模式匹配在性能上表现良好,但如果有大量不同类型的处理,可以考虑使用字典或其他查找结构来优化。

  2. 缓存格式化器:对于频繁使用的自定义类型格式化器,可以考虑缓存起来而不是每次都通过Resolver获取。

  3. 数组预分配:在反序列化时预先分配数组空间,避免频繁的内存分配。

扩展性设计

这个解决方案具有良好的扩展性:

  1. 添加新类型支持:只需在switch-case中添加新的类型处理分支即可支持新的自定义类型。

  2. 模块化设计:可以将不同类型的分支处理逻辑提取到单独的方法中,保持代码整洁。

  3. 条件编译:可以使用条件编译指令为不同平台提供特定的实现。

结论

通过实现自定义的对象数组解析器并正确注册到MessagePack系统中,我们可以有效解决IL2CPP环境下自定义类型序列化的问题。这种方法不仅解决了当前的问题,还提供了一个灵活、可扩展的框架,便于未来添加更多自定义类型的支持。对于Unity开发者来说,理解MessagePack的解析器机制并能够根据项目需求进行定制,是高效使用MessagePack-CSharp的关键技能之一。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
22
6
docsdocs
OpenHarmony documentation | OpenHarmony开发者文档
Dockerfile
168
2.05 K
openHiTLS-examplesopenHiTLS-examples
本仓将为广大高校开发者提供开源实践和创新开发平台,收集和展示openHiTLS示例代码及创新应用,欢迎大家投稿,让全世界看到您的精巧密码实现设计,也让更多人通过您的优秀成果,理解、喜爱上密码技术。
C
94
603
nop-entropynop-entropy
Nop Platform 2.0是基于可逆计算理论实现的采用面向语言编程范式的新一代低代码开发平台,包含基于全新原理从零开始研发的GraphQL引擎、ORM引擎、工作流引擎、报表引擎、规则引擎、批处理引引擎等完整设计。nop-entropy是它的后端部分,采用java语言实现,可选择集成Spring框架或者Quarkus框架。中小企业可以免费商用
Java
8
0
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
C++
199
279
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
954
563
金融AI编程实战金融AI编程实战
为非计算机科班出身 (例如财经类高校金融学院) 同学量身定制,新手友好,让学生以亲身实践开源开发的方式,学会使用计算机自动化自己的科研/创新工作。案例以量化投资为主线,涉及 Bash、Python、SQL、BI、AI 等全技术栈,培养面向未来的数智化人才 (如数据工程师、数据分析师、数据科学家、数据决策者、量化投资人)。
Python
78
71
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Java
60
17
apintoapinto
基于golang开发的网关。具有各种插件,可以自行扩展,即插即用。此外,它可以快速帮助企业管理API服务,提高API服务的稳定性和安全性。
Go
22
0