需要帮助的是最简单的protobuf-net示例2

3

请看以下代码(取自此问题):

  [ProtoContract]
  public class B
  {
    [ProtoMember(1)] public int Y;
  }

  [ProtoContract]
  public class C
  {
    [ProtoMember(1)] public int Y;
  }

  class Program
  {
    static void Main()
    {
      object b = new B { Y = 2 };
      object c = new C { Y = 4 };
      using (var ms = new MemoryStream())
      {
        Serializer.SerializeWithLengthPrefix(ms, b, PrefixStyle.Base128);
        Serializer.SerializeWithLengthPrefix(ms, c, PrefixStyle.Base128);
        ms.Position = 0;
        var b2 = Serializer.DeserializeWithLengthPrefix<B>(ms, PrefixStyle.Base128);
        Debug.Assert(((B)b).Y == b2.Y);
        var c2 = Serializer.DeserializeWithLengthPrefix<C>(ms, PrefixStyle.Base128);
        Debug.Assert(((C)c).Y == c2.Y);
      }
    }
  }

很明显,代码有误,因为 b c 被声明为对象,但我使用通用的 Serializer.Serialize<T>方法对它们进行序列化:
System.ArgumentOutOfRangeException occurred
  Message=Specified argument was out of the range of valid values.
Parameter name: index
  Source=protobuf-net
  ParamName=index
  StackTrace:
       at ProtoBuf.Meta.BasicList.Node.get_Item(Int32 index)
  InnerException: 

如果我将b重新声明为B并将c重新声明为C,则一切正常。但是,我需要它们声明为object,因此我认为我必须使用非泛型方法Serializer.NonGeneric.SerializeWithLengthPrefix对它们进行序列化。问题是,我不理解该方法期望的额外fieldNumber参数的含义。有人能解释一下这是什么以及我应该如何在这里使用它吗?
我使用protobuf-net v2。
谢谢。
编辑:
我通过添加以下代码成功地使其工作:
RuntimeTypeModel.Default.Add(typeof(object), false).AddSubType(1, typeof(B)).AddSubType(2, typeof(C));

虽然它可以工作,但问题是我需要在编译时知道序列化中使用的类型(B = 1,C = 2),这对我来说很糟糕。有更好的方法吗?
编辑2
好的,我已经像这样更改了代码:
public class GenericSerializationHelper<T> : IGenericSerializationHelper
{
  void IGenericSerializationHelper.SerializeWithLengthPrefix(Stream stream, object obj, PrefixStyle prefixStyle)
  {
    Serializer.SerializeWithLengthPrefix(stream, (T)obj, prefixStyle);
  }
}

public interface IGenericSerializationHelper
{
  void SerializeWithLengthPrefix(Stream stream, object obj, PrefixStyle prefixStyle);
}

...

static void Main()
{
  var typeMap = new Dictionary<Type, IGenericSerializationHelper>();
  typeMap[typeof(B)] = new GenericSerializationHelper<B>();
  typeMap[typeof(C)] = new GenericSerializationHelper<C>();

  object b = new B { Y = 2 };
  object c = new C { Y = 4 };
  using (var ms = new MemoryStream())
  {
    typeMap[b.GetType()].SerializeWithLengthPrefix(ms, b, PrefixStyle.Base128);
    typeMap[c.GetType()].SerializeWithLengthPrefix(ms, c, PrefixStyle.Base128);
    ms.Position = 0;
    var b2 = Serializer.DeserializeWithLengthPrefix<B>(ms, PrefixStyle.Base128);
    Debug.Assert(((B)b).Y == b2.Y);
    var c2 = Serializer.DeserializeWithLengthPrefix<C>(ms, PrefixStyle.Base128);
    Debug.Assert(((C)c).Y == c2.Y);
  }
}

现在,为了序列化对象,我不需要任何编译时映射,只需跳转到相应的通用方法。当然,我知道在这个方案中,在反序列化时必须知道类型,这仍然是个问题。
1个回答

2
如果您正在序列化同质数据,则字段编号基本上不相关,略有一个小警告,即如果将其保留为1(又名Serializer.ListItemTag),则将其读回作为列表非常容易(但无论哪种方式都很容易)。在异构数据的情况下,例如此示例-存在专门设计用于此目的的非通用API(实际上,在v2中,所有API均为非通用-API只是将通用API转发到非通用-API)。通过传递TypeResolver,您可以告诉它如何实时解释遇到的任何标记(在流的根处)。如果选择返回给定标记的null,则假定您对该对象不感兴趣并完全跳过它(在下面的示例中,它显然会崩溃,但这只是因为示例代码很简单):
// I'm giving the example in terms of the v2 API, because there is a bug in the 
// Serializer.NonGeneric code in the beta - simply, in the first beta cut this
// doesn't correctly forward to the type-model. This will be fixed ASAP.
TypeModel model = RuntimeTypeModel.Default;
using (var ms = new MemoryStream())
{
    var tagToType = new Dictionary<int, Type>
    {  // somewhere, we need a registry of what field maps to what Type
        {1, typeof(B)}, {2, typeof(C)}
    };
    var typeToTag = tagToType.ToDictionary(pair => pair.Value, pair => pair.Key);

    object b = new B { Y = 2 };
    object c = new C { Y = 4 };
    // in v1, comparable to Serializer.NonGeneric.SerializeWithLengthPrefix(ms, b, PrefixStyle.Base128, typeToTag[b.GetType()]);
    model.SerializeWithLengthPrefix(ms, b, null, PrefixStyle.Base128, typeToTag[b.GetType()]);
    model.SerializeWithLengthPrefix(ms, c, null, PrefixStyle.Base128, typeToTag[c.GetType()]);
    ms.Position = 0;

    // in v1, comparable to Serializer.NonGeneric.TryDeserializeWithLengthPrefix(ms, PrefixStyle.Base128, key => tagToType[key], out b2);
    object b2 = model.DeserializeWithLengthPrefix(ms, null, null, PrefixStyle.Base128, 0, key => tagToType[key]);
    object c2 = model.DeserializeWithLengthPrefix(ms, null, null, PrefixStyle.Base128, 0, key => tagToType[key]);

    Assert.AreEqual(((B)b).Y, ((B)b2).Y);
    Assert.AreEqual(((C)c).Y, ((C)c2).Y);
}

我仍然需要完全理解它。谢谢。 - mark

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