Наследование протобуфов и дженерики - PullRequest
3 голосов
/ 01 августа 2011

Я пытаюсь использовать сеть ProtoBuf для сериализации дерева объектов с классами в следующем формате:

[ProtoContract]
class MySpecialCollectionList<T> : List<MySpecialCollection<T>>
{
    [ProtoMember(1)]
    public string Name { get; set; }
}

[ProtoContract]
class MySpecialCollection<T> : List<Special<T>>
{
    [ProtoMember(1)]
    public string Name { get; set; }
}

[ProtoContract]
class Special<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public string Description { get; set; }

    [ProtoMember(3)]
    private readonly T _source; 
    T Source { get { return _source; } }

    private Special()
    {
    }

    public Special(T source) 
    { 
        _source = source; 
    }
}

interface IBeast
{
    string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
    [ProtoMember(1)]
    public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
    [ProtoMember(1)]
    public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
    [ProtoMember(1)]
    public string Name { get; set; }
}

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
        using (var fs = File.Create(@"c:\temp\protobuftest.bin"))
        {
            Serializer.Serialize(fs, collectionList);

            fs.Close();
        }
    }

    private MySpecialCollectionList<IBeast> GetSpecialCollectionList()
    {
        var ant = new Ant() { Name = "Mr Ant" };
        var cat = new Cat() { Name = "Mr Cat" };
        var dog = new Dog() { Name = "Mr Dog" };

        var Special = new Special<IBeast>(ant);

        var specialCollection1 = new MySpecialCollection<IBeast>() {
            {new Special<IBeast>(ant)},
            {new Special<IBeast>(cat)},
            {new Special<IBeast>(dog)}
        };
        specialCollection1.Name = "Special Collection1";


        var specialCollection2 = new MySpecialCollection<IBeast>() {
            {new Special<IBeast>(ant)},
            {new Special<IBeast>(dog)}
        };
        specialCollection2.Name = "Special Collection2";

        var specialCollectionList = new MySpecialCollectionList<IBeast>() {
            specialCollection1, specialCollection2 };

        specialCollectionList.Name = "Special Collection List";
        return specialCollectionList;
    }
}

Обратите внимание, что класс, который я сериализирую (MySpecialCollectionList<T>), получен из List<SomeOtherClass<T>>, а не просто List<T>.

Я изо всех сил пытаюсь выяснить, где поместить атрибуты "ProtoInclude", чтобы заставить это сериализовать все элементы в MySpecialCollectionList. Любая помощь будет высоко ценится.

1 Ответ

4 голосов
/ 01 августа 2011

Наследование здесь не является проблемой, так как даже если A : B, то Foo<A> : Foo<B> неверно. Обратите внимание, что protobuf-net не будет использовать конструктор не по умолчанию, хотя возможно пропустить конструктор, привязав его непосредственно к полю (даже readonly). Хотя у вас может быть 6 T, я не вижу (из кода), что когда-либо возникнут сомнения в том, какой закрытый тип вы намереваетесь, и если закрытый тип известен, вам следует установить.

Если у вас есть Foo<SomeBaseClass> и несколько конкретных типов, унаследованных от SomeBaseClass, то маркеры будут на SomeBaseClass.

Однако, если у вас есть конкретный сценарий, который я могу использовать для воспроизведения вашей проблемы, я с удовольствием посмотрю.


Обновлена ​​повторная редакция:

В примере выделено несколько ключевых моментов:

  • Как и большинство API связывания, XmlSerializer и IIRC DataContractSerializer, элемент представляет собой либо список xor элемент со значениями ; если коллекция (что-то реализующее IList) сама имеет свойства, они не будут сериализованы; инкапсуляция здесь предпочтительнее наследования, то есть то, что имеет a Name, а имеет список (а не имеет a Name и - это список)
  • protobuf-net v1 не поддерживает сериализацию на основе интерфейса; v2 делает , но, как и в случае с XmlSerializer и DataContractSerializer, вам нужно явно указать ему, чего ожидать; довольно мило, однако, мы можем переместить [ProtoMember] на сам интерфейс

Вот полностью рабочая версия в v2:

using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
class MySpecialCollectionList<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }

    private readonly List<MySpecialCollection<T>> items = new List<MySpecialCollection<T>>();
    [ProtoMember(2)]
    public List<MySpecialCollection<T>> Items { get { return items; } }
}

[ProtoContract]
class MySpecialCollection<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }

    private readonly List<Special<T>> items = new List<Special<T>>();
    [ProtoMember(2)]
    public List<Special<T>> Items { get { return items; } }
}

[ProtoContract]
class Special<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public string Description { get; set; }

    [ProtoMember(3)]
    private readonly T _source;
    T Source { get { return _source; } }

    private Special()
    {
    }

    public Special(T source)
    {
        _source = source;
    }
}
[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
[ProtoInclude(4, typeof(Dog))]
interface IBeast
{
    [ProtoMember(1)]
    string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
    public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
    public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
    public string Name { get; set; }
}

public static class Form1
{

    private static void Main()
    {
        MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
        var copy = Serializer.DeepClone(collectionList);
    }

    private static MySpecialCollectionList<IBeast> GetSpecialCollectionList()
    {
        var ant = new Ant() { Name = "Mr Ant" };
        var cat = new Cat() { Name = "Mr Cat" };
        var dog = new Dog() { Name = "Mr Dog" };

        var Special = new Special<IBeast>(ant);

        var specialCollection1 = new MySpecialCollection<IBeast>() {Items =
            {new Special<IBeast>(ant),
            new Special<IBeast>(cat),
            new Special<IBeast>(dog)}
        };
        specialCollection1.Name = "Special Collection1";


        var specialCollection2 = new MySpecialCollection<IBeast>()
        {
            Items =
            {new Special<IBeast>(ant),
            new Special<IBeast>(dog)}
        };
        specialCollection2.Name = "Special Collection2";

        var specialCollectionList = new MySpecialCollectionList<IBeast>()
        {
            Items ={
            specialCollection1, specialCollection2 }
        };

        specialCollectionList.Name = "Special Collection List";
        return specialCollectionList;
    }
}
...