Protobuf-net学习笔记

来源:互联网 发布:带宽100m交换机的端口! 编辑:程序博客网 时间:2024/06/05 18:31

废话就不说了。

这里记录三种使用pbn的方式:

1、手动方式

2、动态方式

3、proto文件方式

代码示例是编译不过的,主要是学习交流之用。

1、手动方式

核心类:
ProtoContractAttribute
ProtoMemberAttribute
ProtoBuf.Serializer

需要注意的地方:
继承的表示法,需要在基类上使用 ProtoSub(typeof(SubClass), SubClassTag(int))
类似XML那种标记子类的方式。 XMLInclude。
所以这种使用方式有诸多不便。

使用方式如代码:


using UnityEngine;using UnityEditor;using System.Collections.Generic;using ProtoBuf;using System;using System.IO;using System.Xml.Serialization;namespace StaticUsageDemo{    [ProtoContract]    [Serializable]    public class TestDataTable    {        [ProtoMember(1)]        public TestData[] Data = new TestData[0];    }    [ProtoContract]    [Serializable]    public class TestData    {        [ProtoMember(6, IsRequired = true)]        public float value { get; set; }        [ProtoMember(1, IsRequired = true)]        public int UserID { get; set; }        [ProtoMember(2, IsRequired = false)]        public string UserName { get; set; }        [ProtoMember(3, IsRequired = false)]        public string UserName2 { get; set; }        [ProtoMember(4, IsRequired = false)]        public string UserName3 { get; set; }        [ProtoMember(5, IsRequired = false)]        public string UserName4 { get; set; }    }    public static class StaticUsageDemo    {        [MenuItem("Metadata/Create")]        public static void CreateFile()        {            UGE.Utility.HPTimer.Init();            var dataTable = new TestDataTable();            int count = 100000;            using(UGE.Utility.TimeTracker.Record("Create Data"))            {                List<TestData> list = new List<TestData>(count);                for ( int i =0; i < count; ++i )                {                    list.Add(new TestData                    {                        UserID = 0,                        UserName = "AAA",                        UserName2 = "AAA",                        UserName3 = "AAA",                        UserName4 = "AAA",                        value = 1,                    });                }                dataTable.Data = list.ToArray();            }            //TestDataTable dataXML2;            //{            //    Stream fileXML = File.Create("Tools/Output/TestData.xml");            //    try            //    {            //        XmlSerializer xmler = new XmlSerializer(dataTable.GetType());            //        xmler.Serialize(fileXML, dataTable);            //        // read xml version data;            //        fileXML.Seek(0, SeekOrigin.Begin);            //        dataXML2 = xmler.Deserialize(fileXML) as TestDataTable;            //        Debug.Log("dataXML:" + dataXML2.Data[0].UserName);            //    }            //    finally            //    {            //        fileXML.Close();            //    }            //}            // use data;            {                Stream file = File.Open("Tools/Output/TestData.dat", FileMode.OpenOrCreate);                try                {                    Debug.Log("tableCount:" + dataTable.Data.Length);                    using ( UGE.Utility.TimeTracker.Record("Serialize Data") )                    {                        Serializer.Serialize<TestDataTable>(file, dataTable);                    }                    file.Seek(0, SeekOrigin.Begin);                    TestDataTable table;                    using ( UGE.Utility.TimeTracker.Record("Deserialize Data") )                    {                        table = Serializer.Deserialize<TestDataTable>(file);                    }                    Debug.Log("tableCount:" + table.Data.Length);                    Debug.Log(string.Format("method 2:{0}", table.Data[1].UserName));                }                finally                {                    file.Close();                }            }            Debug.Log(UGE.Utility.TimeTracker.Dump());            return;        }    }}


2、动态方式

核心类:
RuntimeTypeModel
MetaType

动态构建方法:

1、注册一个MetaType

RuntimeTypeModel.Default.Add( System.Type)

2、往MetaType里添加Field

                Type type = mt.Type;                FieldInfo[] fields = type.GetFields((BindingFlags)( BindingFlags.Instance | BindingFlags.Public ));                foreach ( var field in fields )                {                    mt.Add(mt.GetNextFieldNumber(), field.Name);                }

3、构建TypeTree


MetaType.AddSubType (  SubClassTag:int,  SubClassType:System.Type)


需要注意的地方:
1、继承,我测试发现,子类的 tag得是全局唯一
所以我为了记录classTag ,专门弄了一个xml来维护这个事情--当然是通过反射代码 。

2、一旦执行了 Serialize / Deserialize 方法后, MetaType 就 Frozen了, 这之后再添加 Field 或 SubType 就会报错。

3、proto文件方式

1、获取生成工具

编译 protobuf-net 和 ProtoGen 这两个csproj,可以得到一个 protogen.exe ,还有一堆 xlst。这些是用来解析 proto文件,并生成目标代码的。

2、编写一个 proto文件

比如 test.proto
message TestData {    required int32 ID=1;    required string UserName=2;    required string UserName2=3;    required string UserName3=4;    required string UserName4=5;    required string UserName5=6;    required float value=7;}message TestDataTable{  repeated TestData data=1;}

3、使用一个proto文件

protogen -i:test.proto -o:test.cs -ns:UGE.Metadata -p:import=UGE

这句话的意思是, 输入test.proto文件, 给我生成 test.cs 文件, 代码在 namespace UGE.Metadata里, 顺便引用下 using UGE.

例子如下:

//------------------------------------------------------------------------------// <auto-generated>//     This code was generated by a tool.////     Changes to this file may cause incorrect behavior and will be lost if//     the code is regenerated.// </auto-generated>//------------------------------------------------------------------------------// Generated from: test.protonamespace test{  [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"TestData")]  public partial class TestData : global::ProtoBuf.IExtensible  {    public TestData() {}        private int _ID;    [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"ID", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]    public int ID    {      get { return _ID; }      set { _ID = value; }    }    private string _UserName;    [global::ProtoBuf.ProtoMember(2, IsRequired = true, Name=@"UserName", DataFormat = global::ProtoBuf.DataFormat.Default)]    public string UserName    {      get { return _UserName; }      set { _UserName = value; }    }    private string _UserName2;    [global::ProtoBuf.ProtoMember(3, IsRequired = true, Name=@"UserName2", DataFormat = global::ProtoBuf.DataFormat.Default)]    public string UserName2    {      get { return _UserName2; }      set { _UserName2 = value; }    }    private string _UserName3;    [global::ProtoBuf.ProtoMember(4, IsRequired = true, Name=@"UserName3", DataFormat = global::ProtoBuf.DataFormat.Default)]    public string UserName3    {      get { return _UserName3; }      set { _UserName3 = value; }    }    private string _UserName4;    [global::ProtoBuf.ProtoMember(5, IsRequired = true, Name=@"UserName4", DataFormat = global::ProtoBuf.DataFormat.Default)]    public string UserName4    {      get { return _UserName4; }      set { _UserName4 = value; }    }    private string _UserName5;    [global::ProtoBuf.ProtoMember(6, IsRequired = true, Name=@"UserName5", DataFormat = global::ProtoBuf.DataFormat.Default)]    public string UserName5    {      get { return _UserName5; }      set { _UserName5 = value; }    }    private float _value;    [global::ProtoBuf.ProtoMember(7, IsRequired = true, Name=@"value", DataFormat = global::ProtoBuf.DataFormat.FixedSize)]    public float value    {      get { return _value; }      set { _value = value; }    }    private global::ProtoBuf.IExtension extensionObject;    global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)      { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }  }    [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"TestDataTable")]  public partial class TestDataTable : global::ProtoBuf.IExtensible  {    public TestDataTable() {}        private readonly global::System.Collections.Generic.List<TestData> _data = new global::System.Collections.Generic.List<TestData>();    [global::ProtoBuf.ProtoMember(1, Name=@"data", DataFormat = global::ProtoBuf.DataFormat.Default)]    public global::System.Collections.Generic.List<TestData> data    {      get { return _data; }    }      private global::ProtoBuf.IExtension extensionObject;    global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)      { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }  }  }


然后就可以利用这个数据定义来做事情了。

没有测试在这种情况下怎么继承。估计这是正确使用pb的方式,应该有很多文档说明。



我在这里记录的很简单,主要是指明了关键的工作流。

在实际使用的时候,需要根据情况自行安排使用方式,所以再往多写就啰嗦了。

顺带一提我选择的是第二种方式。

各方式速度比较:

100,000 十万条数据,  5个string 1个int32 , 1个float 。数据没有多样化。

ms   Write     Read

方式1  430+   2000+

方式2  1000+  1300+

方式3 我没测过,应该和1类似

和json之类的压缩率比较没意义,pb使用varint,本来就很擅长压缩。

算是大致有个印象吧

0 0