Обратите внимание на следующий код (взят из этого вопроса ):
[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
объявлены как object
, но я сериализую их, используя общий метод 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.
Спасибо.
1027 * EDIT *
Мне удалось заставить его работать с добавлением следующего кода:
RuntimeTypeModel.Default.Add(typeof(object), false).AddSubType(1, typeof(B)).AddSubType(2, typeof(C));
Хотя это работает, проблема в том, что мне нужно знать во время компиляции типы, используемые в сериализации (B = 1, C = 2), что плохо для меня. Есть ли лучший способ?
EDIT2
ОК, я изменил код следующим образом:
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);
}
}
Теперь, чтобы сериализовать объекты, мне не нужно отображать время компиляции, я просто перехожу к соответствующему универсальному методу. Конечно, я понимаю, что в этой схеме я должен знать типы при десериализации, которая все еще облом.