2

我将我的项目翻译为使用 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);
    }

问题: 这是正确的吗?(“信息”被序列化程序覆盖?即 binaryformatter 将无法正常工作?目前我只是尝试使用 protobuf-net,我更愿意让 binaryformatter 也能正常工作)

2.关于使用ProtoInclude和RuntimeTypeModel.Default

假设我们有作为类基础的 Message类:Ack 类、HandshakeClientInfo 类... Command 类。如果我们想序列化 Message 的子类,正如我所读到的那样:protobuf-net 的 [ProtoInclude(1, "MyClass")] 不起作用 ,我们应该使用 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)) 的变体;似乎更可取。

问题: 但我不明白它应该放在代码中的什么位置?在构造函数或....?可以同时使用ProtoIncludeRuntimeTypeModel.Default吗?

4

1 回答 1

5

首先,我需要非常清楚 protobuf-net 和ISerializable. 首先,ISerializable仅由 使用BinaryFormatter。protobuf-net 永远不会寻找这个接口,也永远不会直接使用这个接口。所以,你可能会问,为什么 protobuf-net 甚至会提到这个?

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

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;
于 2012-09-22T20:01:59.140 回答