You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Protobuf-net 3.2.52中实现WPlane实例跨类引用反序列化复用的技术问询

Protobuf-net 3.2.52中实现WPlane实例跨类引用反序列化复用的技术问询

我完全理解你现在的困境:序列化时B、C、D三个类的_Plane字段明明指向同一个WPlane实例,但反序列化后每个类都拿到了独立的WPlane对象,而且protobuf-net 3.x版本里原先的AsReference属性已经被移除,找不到直接替代的配置项。下面我会结合protobuf-net 3.2.52的特性,给你一步步的解决方案,同时还会指出你当前代码里容易被忽略的反序列化漏洞:


第一步:先修复WPlane类的反序列化逻辑(关键前提)

我注意到你当前的WPlane类只实现了序列化前的字节数组转换,但没有处理反序列化后的对象恢复逻辑!如果不补充这部分代码,即使反序列化得到了WPlane实例,它的Plane属性会是null,完全无法使用。请给WPlane类添加反序列化后处理方法:

[ProtoContract(SkipConstructor = true, ReferenceTracked = true)]
public class WPlane
{
    private bool _IsSerialized;
    public WPlane(Plane plane)
    {
        _IsSerialized = false;
        Plane = plane;
    }

    [ProtoMember(2)]
    private byte[] _Plane_BA;
    public Plane Plane { get; set; }

    [ProtoBeforeSerialization]
    private void OnSerializing()
    {
        if (!_IsSerialized)
        {
            _Plane_BA = SerializationHelper.EyeshotObjectSerialize(Plane);
            _IsSerialized = true;
        }
    }

    // 新增反序列化后处理方法
    [ProtoAfterDeserialization]
    private void OnDeserialized()
    {
        if (_Plane_BA != null)
        {
            Plane = SerializationHelper.EyeshotObjectDeserialize(_Plane_BA);
            _IsSerialized = true; // 标记为已序列化,避免后续重复处理
        }
    }
}

第二步:启用WPlane类型的引用追踪(核心解决方案)

在protobuf-net 3.x中,AsReference被替换为引用追踪配置,有两种方式实现你的需求:

方案1:针对WPlane类型精准启用引用追踪(推荐)

给WPlane类的[ProtoContract]属性添加ReferenceTracked = true(已经在上面的代码示例中加上了),这样只有WPlane类型会被protobuf-net追踪实例引用,序列化时会记录实例的唯一标识,反序列化时自动复用同一个实例。

然后在序列化/反序列化时,需要使用SerializerOptions启用显式引用追踪:

// 序列化
var options = new SerializerOptions { 
    ReferencePreservation = ReferencePreservationOptions.Explicit 
};
byte[] serializedData = Serializer.Serialize(options, yourRootObject); // yourRootObject是包含B、C、D的根对象

// 反序列化
var deserializedObject = Serializer.Deserialize<YourRootType>(options, serializedData);

这个方案的优势是精准控制,不会影响其他类型的序列化行为,避免不必要的体积开销。

方案2:全局启用所有类型的引用追踪

如果你希望所有支持的类型都启用引用追踪,可以直接设置ReferencePreservationOptions.All

var options = new SerializerOptions { 
    ReferencePreservation = ReferencePreservationOptions.All 
};
// 序列化反序列化代码同上

优点是配置简单,无需修改类的属性;缺点是会对所有类型启用引用追踪,可能增加序列化后的字节体积,或者引入潜在的循环引用问题(如果你的对象结构中有循环引用的话)。


第三步:验证效果

完成上述修改后,你可以在反序列化后通过object.ReferenceEquals方法验证:

// 假设deserializedObject包含B、C、D实例
bool areSameInstance = object.ReferenceEquals(deserializedObject.B._Plane, deserializedObject.C._Plane);
// 预期返回true,说明两个_WPlane指向同一个实例

额外注意事项

  1. 确保你的根对象(包含B、C、D的对象)也正确标记了[ProtoContract],否则protobuf-net无法正确序列化整个对象树;
  2. SkipConstructor = true的配置是正确的,因为你的类都有带参数的构造函数,protobuf-net会跳过构造函数直接赋值私有字段;
  3. 如果WPlane实例在序列化前被修改过,记得重置_IsSerialized为false,否则下次序列化时会使用旧的_Plane_BA数据。

内容来源于stack exchange

火山引擎 最新活动