Protobuf 继承和泛型

Posted

技术标签:

【中文标题】Protobuf 继承和泛型【英文标题】:Protobuf Inheritance and Generics 【发布时间】:2011-08-01 14:02:33 【问题描述】:

我正在尝试使用 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&lt;T&gt;) 是如何从 List&lt;SomeOtherClass&lt;T&gt;&gt; 派生的,而不仅仅是 List&lt;T&gt;

我正在努力找出在哪里放置“ProtoInclude”属性以使其序列化 MySpecialCollectionList 中的所有项目。任何帮助将不胜感激。

【问题讨论】:

您能指出您使用的是哪个版本的protobuf-net吗? 我使用的是版本 1.0.0.282,但我要升级到版本 2 【参考方案1】:

这里的继承不是问题,因为即使A : B 也不是Foo&lt;A&gt; : Foo&lt;B&gt;。请注意,protobuf-net 不会使用非默认构造函数,尽管可以跳过构造函数,直接绑定到字段(甚至readonly)。虽然您可能有 6 个T,但我无法(从代码中)看出您想要使用哪种封闭类型,如果已知封闭类型,则应该设置它。

如果你有一个Foo&lt;SomeBaseClass&gt; 和一些从SomeBaseClass 继承的具体类型,那么标记将在SomeBaseClass 上。

但是,如果您有一个具体的场景可以用来重现您的问题,我会很乐意看看。


更新重新编辑:

示例中引出了几个关键点:

与大多数绑定 API、XmlSerializer 和 IIRC DataContractSerializer 一样,项目是任一列表xor具有值的项目;如果一个集合(实现IList的东西)本身有属性,它们将不会被序列化; encapsulation 在此优先于继承,即 具有 Name 并且 具有 列表(而不是 具有 一个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;
    

【讨论】:

感谢您的回复。我已经修改了我的问题中的代码,使其更加明确。当我尝试序列化对象时,它会序列化***对象的“名称”属性,但不会继续序列化列表中的项目。 @EasyTimer - 很快就会看到 谢谢马克。抱歉,代码示例有点大,我尽量保持简单。 @EasyTimer 不用担心大小;我宁愿“大但允许我重现问题”而不是“小但什么都不告诉我” 这很有意义。 Protobuf 似乎是一种完美的序列化方式。我将下载版本 2,以便我可以使用该界面。非常感谢您的快速、清晰的回复。我期待认真使用 Protobuf。

以上是关于Protobuf 继承和泛型的主要内容,如果未能解决你的问题,请参考以下文章

ProtoBuf-Net ProtoInclude 泛型类型子类

Java中的继承和泛型

使用protobuf-net继承时如何选择字段号?

Protobuf-net.Grpc 服务契约继承

Java Protobuf (ver. 2.4.1) 和 Protobuf-net (ver. r480) 继承兼容性

用于继承的 Protobuf-net .proto 文件生成