Учитывая эти определения классов:
[ProtoContract, ProtoInclude(2, typeof(Class2))]
class Class1
{
[ProtoMember(1)]
public string Field1 { get; set; }
}
[ProtoContract]
class Class2 : Class1
{
[ProtoMember(1)]
public string Field2 { get; set; }
}
Я пытаюсь добиться следующего:
using (var ms = new MemoryStream())
{
var c1 = new Class1 { Field1 = "hello" };
Serializer.Serialize<Class1>(ms, c1);
ms.Position = 0;
var c2 = Serializer.Deserialize<Class2>(ms);
}
Но я получаю следующее исключение: Unable to cast object of type 'ProtoBufTest.Class1' to type 'ProtoBufTest.Class2'
Я не совсем понимаю проблему; я понимаю, что при десериализации Protobuf должен просто рассматривать входящий поток как совокупность байтов, так почему он, по-видимому, сначала десериализуется в объект Class1
, а затем пытается поместить его в Class2
?
Добавив [ProtoInclude(...)]
, вы сказали protobuf-net обрабатывать Class1
и Class2
таким образом, чтобы позволить наследованию работать. Независимо от того, задаете ли вы <Class1>
или <Class2>
, protobuf-net начнет работу с базового типа и будет создан вверх; по существу, вы стали моделью (в протобуфе):
message Class1 {
optional string Field1 = 1;
// the following represent sub-types; at most 1 should have a value
optional Class2 Class2 = 2;
}
message Class2 {
optional string Field2 = 1;
}
Если .Class2
экземпляр .Class2
, он будет десериализоваться как Class2
; иначе он будет десериализоваться как Class1
. Это преднамеренно, так что, если вы сериализуете Class1
вы возвращаете Class1
, и поэтому, если вы сериализуете Class2
вы получите Class2
.
Если вы хотите рассмотреть два типа отдельно, не добавляйте [ProtoInclude]
. Фактически, в этом случае вы даже можете использовать Serializer.ChangeType
для сериализации/десериализации в оба конца:
var c1 = new Class1 { Field1 = "hello" };
var c2 = Serializer.ChangeType<Class1, Class2>(c1);
Обратите внимание: в этом случае я бы задался вопросом, почему в первую очередь были отношения наследования. Из того, что вы делаете, кажется, что вы на самом деле просто хотите:
[ProtoContract]
class Class1
{
[ProtoMember(1)]
public string Field1 { get; set; }
}
[ProtoContract]
class Class2
{
[ProtoMember(1)]
public string Field2 { get; set; }
}
(хотя я понятия не имею, почему)
Потому что вы отправляете экземпляр Class1, а не Class2. Если вы вызываете метод с экземпляром Class1, вы не можете магически преобразовать его в Class2.
Вы можете создать новый экземпляр класса 2 и заполнить его членами класса Class1. Но это выглядит плохой дизайн.