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

Protobuf-net反序列化报错:引用跟踪对象反序列化期间变更引用

问题分析与解决

这确实是protobuf-net在引用跟踪与代理(Surrogate)结合使用时的特定行为限制,并非bug,而是两者的设计逻辑冲突导致的。

为什么会报错?

当你给IExecEnv设置AsReferenceDefault = true时,protobuf-net会启用引用跟踪机制:在反序列化过程中,框架会先为该类型创建一个临时占位对象(用于记录引用关系),然后再尝试填充对象的实际数据或完成代理转换。

但你的代理ExecEnvSurrogate的FromSurrogate方法直接返回了一个已存在的单例ExecEnv.DefaultEnv,这就导致框架发现:原本跟踪的临时占位对象的引用,被替换成了另一个完全不同的已存在实例——这违反了引用跟踪机制的核心假设:对象的引用在反序列化过程中必须保持稳定,不能中途被替换,因此抛出了"A reference-tracked object changed reference during deserialization"错误。

而当你移除AsReferenceDefault = true时,引用跟踪被关闭,框架不再跟踪对象引用,直接接受代理返回的单例,自然就不会报错。

解决方案

根据你的场景(ExecEnv是单例),推荐以下几种处理方式:

1. 直接移除引用跟踪配置(最简洁)

既然你的IExecEnv实现是全局单例,根本不需要引用跟踪(单例永远只有一个实例,不存在重复引用需要跟踪的场景),直接删除metaType.AsReferenceDefault = true即可,这也是你已经验证可行的方案。

2. 针对单例类型覆盖引用跟踪设置

如果你的项目中其他IExecEnv实现需要引用跟踪,仅ExecEnv单例不需要,可以单独给ExecEnv类型设置引用跟踪为false:

var execEnvMeta = RuntimeTypeModel.Default.Add(typeof(ExecEnv), false);
execEnvMeta.AsReferenceDefault = false;

3. 精准控制字段的引用跟踪

不要给整个接口设置默认引用跟踪,而是在需要引用跟踪的字段上单独标记[ProtoMember(..., AsReference = true)],比如修改WithExecEnv的字段配置:

private class WithExecEnv
{
    [ProtoMember(1, AsReference = true)]
    private IExecEnv _env;

    // ... 其他代码
}

同时移除接口的AsReferenceDefault = true配置,这样只有特定字段启用引用跟踪,避免和代理单例冲突。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:01:14