You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用protobuf-net序列化实现ITypedList的已知类型object[]数组?

序列化实现方案

针对你的场景(基于ITypedList的object[]行数据结构),有几种可行的protobuf-net序列化方案,各有优劣,下面逐一分析:


1. 优化版包装类方案(推荐)

你提出的ValueWrapper思路是可行的,但可以通过OneOf特性优化内存占用和序列化体积,避免冗余字段:

[ProtoContract]
public enum AvailableTypes
{
    [ProtoEnumValue(0)] Int,
    [ProtoEnumValue(1)] String,
    [ProtoEnumValue(2)] Bool
}

[ProtoContract]
public struct ValueWrapper // 用struct减少堆内存分配
{
    [ProtoMember(1)]
    public AvailableTypes Type { get; set; }

    [ProtoOneOf("Value")]
    [ProtoMember(2)]
    public int IntValue { get; set; }

    [ProtoOneOf("Value")]
    [ProtoMember(3)]
    public string StringValue { get; set; }

    [ProtoOneOf("Value")]
    [ProtoMember(4)]
    public bool BoolValue { get; set; }

    // 便捷转换方法
    public static implicit operator ValueWrapper(int value) => new() { Type = AvailableTypes.Int, IntValue = value };
    public static implicit operator ValueWrapper(string value) => new() { Type = AvailableTypes.String, StringValue = value };
    public static implicit operator ValueWrapper(bool value) => new() { Type = AvailableTypes.Bool, BoolValue = value };
    public object ToObject() => Type switch
    {
        AvailableTypes.Int => IntValue,
        AvailableTypes.String => StringValue,
        AvailableTypes.Bool => BoolValue,
        _ => throw new InvalidOperationException("未知类型")
    };
}

优势:

  • 用struct替代class,减少堆内存分配,提升性能
  • ProtoOneOf确保同一时间只有一个值字段被序列化,大幅减小序列化后的体积
  • 隐式转换方法简化object[]和ValueWrapper[]之间的映射

转换示例:

// object[]转ValueWrapper[]
var valueWrappers = rowArray.Select(obj => (ValueWrapper)obj).ToArray();

// ValueWrapper[]转object[]
var originalArray = valueWrappers.Select(wrapper => wrapper.ToObject()).ToArray();

2. 利用RuntimeTypeModel实现多态序列化

如果不想引入包装类,可以通过protobuf-net的RuntimeTypeModel注册所有已知类型,直接序列化object[]:

步骤:

  1. 注册所有可能的类型(从ITypedList获取):
var model = RuntimeTypeModel.Default;
model.Add(typeof(object), false)
    .AddSubType(1, typeof(int))
    .AddSubType(2, typeof(string))
    .AddSubType(3, typeof(bool));
  1. 直接序列化object[]:
using var stream = new MemoryStream();
model.Serialize(stream, rowArray);
stream.Position = 0;
var deserialized = (object[])model.Deserialize(stream, null, typeof(object[]));

优势:

  • 无需额外包装类,代码更简洁
  • 直接操作原始object[],避免转换开销

注意事项:

  • 必须提前注册所有可能的类型(可通过ITypedList.GetItemProperties()遍历获取并注册)
  • 序列化后的体积略大于OneOf包装类方案,因为protobuf-net会为每个对象添加类型标识字段
  • 若类型较多,注册和维护成本会增加

3. 基于ITypedList的动态序列化(高性能但复杂)

如果追求极致性能,可以直接针对每个元素的类型调用对应的序列化器,手动构建protobuf消息:

思路:

  • 通过ITypedList获取每个元素的类型
  • 利用protobuf-net的Serializer.NonGeneric.Serialize方法逐个序列化元素
  • 序列化时记录每个元素的类型索引,反序列化时按索引还原类型

示例代码片段:

// 提前建立类型与索引的映射字典
private readonly Dictionary<Type, int> _typeToIndex = new()
{
    { typeof(int), 0 },
    { typeof(string), 1 },
    { typeof(bool), 2 }
};
private readonly Dictionary<int, Type> _indexToType = new()
{
    { 0, typeof(int) },
    { 1, typeof(string) },
    { 2, typeof(bool) }
};

// 序列化
using var stream = new MemoryStream();
// 先写入元素数量
Serializer.SerializeWithLengthPrefix(stream, rowArray.Length, PrefixStyle.Base128);
foreach (var obj in rowArray)
{
    var typeIndex = _typeToIndex[obj.GetType()];
    Serializer.SerializeWithLengthPrefix(stream, typeIndex, PrefixStyle.Base128);
    Serializer.NonGeneric.SerializeWithLengthPrefix(stream, obj, PrefixStyle.Base128);
}

// 反序列化
stream.Position = 0;
var length = Serializer.DeserializeWithLengthPrefix<int>(stream, PrefixStyle.Base128);
var result = new object[length];
for (int i = 0; i < length; i++)
{
    var typeIndex = Serializer.DeserializeWithLengthPrefix<int>(stream, PrefixStyle.Base128);
    var type = _indexToType[typeIndex];
    result[i] = Serializer.NonGeneric.DeserializeWithLengthPrefix(type, stream, PrefixStyle.Base128);
}

优势:

  • 内存分配最少,性能最优
  • 完全自定义序列化逻辑,适配复杂场景

劣势:

  • 实现复杂度高,需要手动处理类型映射和消息结构
  • 代码维护成本高,不易扩展

方案选择建议

  • 如果追求开发效率和代码简洁性:选择多态序列化方案(方案2)
  • 如果追求序列化体积和内存效率:选择优化版包装类方案(方案1)
  • 如果追求极致性能:选择动态序列化方案(方案3)

内容的提问来源于stack exchange,提问作者Wolfgang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 14:52:23