Protobuf-net学习笔记

废话就不说了。

这里记录三种使用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.proto
namespace 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,本来就很擅长压缩。

算是大致有个印象吧

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值