使用方法:1。使用Serializer.Merge / Serialize&amp ;; ISerializable 2. ProtoInclude with RuntimeTypeModel.Default?

时间:2012-09-22 10:07:48

标签: inheritance serialization protocol-buffers protobuf-net iserializable

我将我的项目翻译为使用protobuf-net而不是BinaryFormatter。 看起来缺少文档http://code.google.com/p/protobuf-net/w/list 我还查看了http://code.google.com/p/protobuf-net/source/browse/的一些例子 但有些事情对我来说仍然不明确,这也是我决定在这里问的原因:

1。关于ISerializable和Serializer.Merge / Serialize

如果我们从ISerializable继承以进行特定的序列化。 正如我读到的那样:ProtoBuf-Net ProtoInclude Generic Type Subclass 我们必须使用钩子Serializer.Merge / Serialize;

假设我们有课程:

[Serializable]
[ProtoContract]
public class Anchor : ISerializable
{       
    [ProtoMember(1)]
    public int id;

    [ProtoMember(2)]
    public Ship ship;
    ...
 }

Serializer.Merge(info,this); 必须添加到构造函数 Anchor(SerializationInfo info,StreamingContext context)

Serializer.Serialize(info,this); 添加到 void GetObjectData(SerializationInfo info,StreamingContext context)

所以,我们有:

    protected Anchor(SerializationInfo info, StreamingContext context)
    {
        //for binaryformatter:            
        Type myType = typeof(Anchor);
        foreach (SerializationEntry e in info)
        {
            FieldInfo f = myType.GetField(e.Name,BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Public|BindingFlags.Instance);
            f.SetValue(this,e.Value);
        }

        //added for protobuf-net:
        Serializer.Merge(info, this);
   }

    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        //for binaryformatter:
        foreach (FieldInfo f in typeof(Anchor).GetFields(BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance))
        {
            if ((f.Attributes&FieldAttributes.NotSerialized)==0)
                info.AddValue(f.Name,f.GetValue(this));
        }

       //added for protobuf-net:
        Serializer.Serialize(info, this);
    }

问题: 它是否正确? (“info”被Serializer覆盖?即binaryformatter无法正常工作?在目前的时间点我只是尝试使用protobuf-net而我宁愿让binaryformatter也能正常工作)

2。关于使用ProtoInclude和RuntimeTypeModel.Default

假设我们有类Message ,它是类的基础:类Ack,类HandshakeClientInfo ...类命令。 如果我们想要序列化Message的孩子,请阅读:protobuf-net's [ProtoInclude(1, "MyClass")] did not work 如果我们在编译时知道子类型,我们应该使用ProtoInclude(“告诉”基类:类消息关于它的子节点) - 这没关系。

对于那些我们无法在编译时确定的类型的孩子(因为它在另一个项目中),我们应该使用 RuntimeTypeModel.Default [typeof(Message)]。AddSubType(207,typeof(Command)); 或者使用Type.AssemblyQualifiedName: [ProtoInclude(207,“Trainer.Commands.Command,Simulator,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null”)]

[Serializable]
[ProtoContract]
[ProtoInclude(200, typeof(Ack))]
[ProtoInclude(201, typeof(HandshakeClientInfo))]
[ProtoInclude(202, typeof(HandshakeReadyToSimulation))]
[ProtoInclude(203, typeof(FileMessage))]
[ProtoInclude(204, typeof(HandshakeResponse))]
[ProtoInclude(205, typeof(Sync))]
[ProtoInclude(206, typeof(HandshakeSimulationStart))]    

public abstract class Message {

    [ProtoMember(1)]
    public byte Sender;
...
}

我使用protobuf-net v2(r580)和 RuntimeTypeModel.Default [typeof(Message)]的变体.AddSubType(207,typeof(Command)); 似乎更可取。

问题: 但我不明白它应该放在代码中的哪个位置?在构造函数或....? 是否允许一起使用 ProtoInclude RuntimeTypeModel.Default

1 个答案:

答案 0 :(得分:5)

首先,我需要明确说明protobuf-net与ISerializable之间的关系。首先,ISerializable仅由BinaryFormatter使用。 protobuf-net永远不会寻找这个接口,永远不会直接使用这个接口。所以,你可能会问,为什么protobuf-net甚至会提到这个?

答案:因为有些人的现有代码需要才能使用BinaryFormatter,但想要在内部使用其他内容。一个很好的例子可能是使用远程处理的现有代码。在这里,protobuf-net可以用来实现 ISerializable,基本上取代BinaryFormatter的内容。典型用法如下:

protected Anchor(SerializationInfo info, StreamingContext context)
{
    Serializer.Merge(info, this);
}

public virtual void GetObjectData(SerializationInfo info,
    StreamingContext context)
{
    Serializer.Serialize(info, this);
}

然后在内部使用protobuf-net; protobuf-net将序列化对象(和任何子数据),并将数据存储在单个字段中;在反序列化期间,它将扭转这一局面。 ISerializable接口不用于向protobuf-net添加其他信息。在大多数情况下,如果您想单独支持protobuf-net BinaryFormatter,那么就不会有Serializer / protobuf-net代码构造函数/ GetObjectData,您也不需要对已经(或不存在)的任何现有ISerializable实现进行任何更改。这仅适用于非常具体的场景,您希望从内部更改BinaryFormatter 的工作方式。在这种情况下,您通常可以完全控制protobuf-net。


关于子类型的问题;是的,您可以将[ProtoInclude]RuntimeTypeModel.Default结合使用。基本上,默认情况下(可以调整),protobuf-net第一次看到一个类型时,它会检查属性。如果有,它将使用这些属性来配置模型。但是,您仍然可以对配置进行任何更改,直到它必须序列化/反序列化该类型,此时它会将配置信息烘焙到执行工作的策略中。一旦决定了策略,它就不喜欢你改变配置。

因此,配置模型的最佳时间是:在应用启动时。或者至少,在你开始做任何有趣的事情之前。为了完整起见,我应该注意,如果你想完全手动配置模型,你也可以要求它忽略这些属性(这是非常罕见的)。

所以你的行:

RuntimeTypeModel.Default[typeof(Message)].AddSubType(207, typeof(Command));
在应用启动时

会很好。

在某些(罕见)情况下,您可能会在稍后发现新类型。由于各种非常复杂的原因,在开始序列化后允许更改现有模型并不是很实际,但是:RuntimeTypeModel.Default 只是一个默认实例。在某些更高级的方案中,可能想要做的是维护自己的模型,然后根据需要配置新的更有见识的至。因此,您可以使用:

,而不是使用RuntimeTypeModel.Default
static RuntimeTypeModel serializer = TypeModel.Create();

然后在以后,您可以配置新的设置:

var workInProgress = TypeModel.Create();
// TODO: add whatever you need here
serializer = workInProgress;