protobuf-net继承:派生类隐藏基类属性

4

protobuf-net proto2 c#

我有一个派生类,通过隐藏同名的基类属性来重新定义类型。

我希望将一个基类实例序列化,并作为派生类型进行反序列化:

[ProtoBuf.ProtoContract(Name=@"BaseClassProto")]
[ProtoBuf.ProtoInclude(typeof(DerivedClass), 1000)]
public partial class BaseClass {
  [ProtoBuf.ProtoMember(1, IsRequired = false, Name = @"MyProperty", DataFormat = ProtoBuf.DataFormat.TwosComplement)] 
  public int MyProperty { get; set; }
}

[ProtoBuf.ProtoContract(Name=@"DerivedClassProto")] 
public partial class DerivedClass : BaseClass {
  [ProtoBuf.ProtoMember(1, IsRequired = false, Name = @"MyProperty", DataFormat = ProtoBuf.DataFormat.TwosComplement)] 
  public new MyEnum MyProperty { get; set; }
  }
}

public class Test {
  var baseObject = new BaseClass{ TestString = "TestBaseObject", TestInt = 1 };

  DerivedClass derivedObject;
  using (var stream = new MemoryStream())
  {
    ProtoBuf.Serializer.Serialize(stream, baseObject);
    Debug.WriteLine(stream.Length);
    stream.Seek(0, SeekOrigin.Begin);
    derivedObject = ProtoBuf.Serializer.Deserialize<DerivedClass>(stream);
  }
}

一个类型为 'System.InvalidCastException' 的异常在 protobuf-net.dll 中发生,但未在用户代码中处理。其他信息:无法将类型为 'BaseClass' 的对象转换为类型 'DerivedClass'。
为什么 protobuf-net 尝试将 BaseClass 转换为 DerivedClass
根据 DerivedClass 中的 proto 注释,protobuf-net 不应该直接将消息反序列化为 DerivedClass 吗?
同时,隐藏 BaseClass.IntProperty 是否也会隐藏它的 ProtoMember 注释?从而允许在 DerivedClass 中重新定义 proto index 1
添加附加信息:
尝试使用最小可行测试用例,即使将 DerivedClass 重新定义为尽可能简单的形式:
[ProtoBuf.ProtoContract(Name=@"DerivedClassProto")] 
public partial class DerivedClass : BaseClass {
}

我发现序列化BaseClass / 反序列化DerivedClass仍然会抛出相同的System.InvalidCastException异常。

如果我反序列化成object类型的引用(而不是DerivedClass),底层类型是BaseClass。这解释了类型转换异常,但是这也引出了一个问题:为什么ProtoBuf.Serializer.Deserialize<DerivedClass>()反序列化成BaseClass类型的对象?

1个回答

2
在protobuf-net中,不同层次的层次结构在.proto术语中是单独的消息。子类的标签与基类中的标签无关。在不同层次之间重用标签不是一个概念。我很惊讶所展示的代码实际上能够正常工作,因为它似乎在单个层次(在 MyBaseType 上)中两次使用标签1(一次用于子类型,一次用于属性)。这可能会导致混淆的错误。

啊,那是个打错字了:我已经修改成了[ProtoBuf.ProtoInclude(typeof(MyDerivedType), 1000)] - BaltoStar
在这种情况下,我怀疑是库中的反射错误。重新声明的成员对于反射来说是很麻烦的。如果第二个成员的名称不同,它还会发生吗? - Marc Gravell
如果基类型和子类型契约仅从基类和子类分别确定,那么 [ProtoInclude] 的目的是什么? - BaltoStar
@BaltoStar 为了允许路径到达所有需要处理的级别,特别是在反序列化期间(尽管同样适用于序列化,但至少可以使用.BaseType等)。因此,有一些代码知道基础类型具有哪些字段,并知道预期的子类型; 该代码被调用,执行其操作并移交给下一级,依此类推。两个级别都是必需的,但它们是分开的。 - Marc Gravell
那么,子类型的序列化字节以基本类型消息开头(根据基本类型合同),然后附加一个子类型消息(根据子类型合同)?基本类型字段用标记标记,每个标记包括字段索引和原始类型,并且后跟一个标记,其中包括子类型索引(在基本类型上定义的[ProtoInclude]中),该标记位于实际子类型消息之前? - BaltoStar
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接