Наследование объектов в protobuf-сети

1

Учитывая эти определения классов:

[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?

Теги:
protobuf-net

2 ответа

2
Лучший ответ

Добавив [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; }
}

(хотя я понятия не имею, почему)

  • 0
    Спасибо за это подробное объяснение. На самом деле я бы не потребовал какого-либо ProtoMember в Class2, поскольку мой сценарий использования - десериализация Class1 и добавление его в дополнительные поля перед отправкой клиенту (с полями «flat» в объекте для сериализации JSON). Я пробовал Serializer.ChangeType, но и Field1, и Field2 были нулевыми в результате.
  • 0
    @ThomasWeiss работал нормально здесь ... нужно будет увидеть пример того, как он не работает
Показать ещё 1 комментарий
1

Потому что вы отправляете экземпляр Class1, а не Class2. Если вы вызываете метод с экземпляром Class1, вы не можете магически преобразовать его в Class2.

Вы можете создать новый экземпляр класса 2 и заполнить его членами класса Class1. Но это выглядит плохой дизайн.

  • 0
    При вызове Serializer.Deserialize <Class2> (мс) единственное, что получает Serializer, - это поток, который не ссылается на Class1 (имена типов не сериализованы AFAIK), так почему же здесь появляется Class1?
  • 0
    Согласно stackoverflow.com/questions/947666/… мета-информация для типов включается при использовании атрибута ProtoInclude.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню