這僅是一個輔助方法幫助類,能夠協助小夥伴寫二進制序列化的效率,代碼也還看的過去php
在開始以前,我須要說明的是,若是不是必要,不要使用二進制序列化。由於很難作到版本兼容,若是寫錯了也不知道是哪裏寫錯了,調試難度很大。可是對於性能的提高,其實也不大ide
/// <summary> /// 二進制序列化 /// </summary> interface IBinarySerializable { void Serialize(BinaryWriter binaryWriter); void Deserialize(BinaryReader binaryReader); }
這個接口用於給對象繼承,若是對象繼承了,那麼就方便進行序列化性能
這是一些輔助方法單元測試
沒有用到反射,須要本身手動寫轉換代碼。注意順序測試
static class BinarySerialize { /// <summary> /// 寫入 uin32 列表 /// </summary> /// <param name="binaryWriter"></param> /// <param name="list"></param> public static void WriteUint32List(this BinaryWriter binaryWriter, List<uint> list) { // 格式先寫入列表長度,而後依次寫入內容 binaryWriter.Write(list.Count); foreach (var n in list) { binaryWriter.Write(n); } } /// <summary> /// 讀取 uint32 列表 /// </summary> /// <param name="binaryReader"></param> /// <returns></returns> public static List<uint> ReadUint32List(this BinaryReader binaryReader) { // 讀取長度 var count = binaryReader.ReadInt32(); List<uint> list = new List<uint>(count); for (int i = 0; i < count; i++) { list.Add(binaryReader.ReadUInt32()); } return list; } /// <summary> /// 寫入字符串列表 /// </summary> /// <param name="binaryWriter"></param> /// <param name="stringList"></param> public static void WriteStringList(this BinaryWriter binaryWriter, List<string> stringList) { // 格式先寫入列表長度,而後依次寫入字符串 binaryWriter.Write(stringList.Count); foreach (var str in stringList) { binaryWriter.Write(str); } } /// <summary> /// 寫入可序列化類的列表 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="binaryWriter"></param> /// <param name="list"></param> public static void WriteList<T>(this BinaryWriter binaryWriter, List<T> list) where T : IBinarySerializable { // 先寫入長度 binaryWriter.Write(list.Count); foreach (var binarySerializable in list) { binarySerializable.Serialize(binaryWriter); } } /// <summary> /// 讀取可序列化類的列表 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="binaryReader"></param> /// <returns></returns> public static List<T> ReadList<T>(this BinaryReader binaryReader) where T : IBinarySerializable, new() { // 讀取長度 var count = binaryReader.ReadInt32(); List<T> list = new List<T>(count); for (int i = 0; i < count; i++) { T t = new T(); t.Deserialize(binaryReader); list.Add(t); } return list; } /// <summary> /// 讀取字符串列表 /// </summary> /// <param name="binaryReader"></param> /// <returns></returns> public static List<string> ReadStringList(this BinaryReader binaryReader) { // 先讀取列表長度,而後依次讀取字符串 var count = binaryReader.ReadInt32(); List<string> stringList = new List<string>(count); for (int i = 0; i < count; i++) { var str = binaryReader.ReadString(); stringList.Add(str); } return stringList; } /// <summary> /// 寫入頭信息,默認是須要固定長度的 /// </summary> /// <param name="binaryWriter"></param> /// <param name="head"></param> /// <param name="headLength"></param> public static void WriteHead(this BinaryWriter binaryWriter, string head, int headLength) { var headByte = StringToByteList(head); binaryWriter.WriteByteList(headByte, headLength); } /// <summary> /// 將字符串轉換爲 byte 其中字符串添加長度前綴 /// </summary> /// <param name="str"></param> /// <returns></returns> public static byte[] StringToByteList(string str) { var strByteList = Utf8.GetBytes(str); ushort byteLength = (ushort)strByteList.Length; var newLength = sizeof(ushort) + strByteList.Length; var byteList = new byte[newLength]; var binaryWriter = new BinaryWriter(new MemoryStream(byteList)); binaryWriter.Write(byteLength); binaryWriter.Write(strByteList); return byteList; } /// <summary> /// 寫入二進制寫入固定長度 /// </summary> /// <param name="binaryWriter"></param> /// <param name="source"></param> /// <param name="byteCount"></param> public static void WriteByteList(this BinaryWriter binaryWriter, byte[] source, int byteCount) { var byteList = new byte[byteCount]; Array.Copy(source, 0, byteList, 0, Math.Min(source.Length, byteCount)); binaryWriter.Write(byteList, 0, byteCount); } private static Encoding Utf8 => Encoding.UTF8; /// <summary> /// 讀取固定長度的頭 /// </summary> /// <param name="binaryReader"></param> /// <param name="headLength"></param> /// <returns></returns> public static string ReadHead(this BinaryReader binaryReader, int headLength) { var strByteLength = (ushort)binaryReader.ReadInt16(); var strByteList = binaryReader.ReadBytes(strByteLength); var head = Utf8.GetString(strByteList); var readLength = headLength - sizeof(ushort) - strByteLength; binaryReader.ReadBytes(readLength); return head; } }
這裏的讀寫 Head 也許小夥伴的業務是用不到的,我用這個方法主要是寫入版本號ui
這是單元測試的代碼,只是測試主要使用方法,邊界沒有測試this
[TestClass] public class BinarySerializeTests { private class FakeBinarySerialize : IBinarySerializable, IEquatable<FakeBinarySerialize> { /// <inheritdoc /> public void Serialize(BinaryWriter binaryWriter) { binaryWriter.Write(F1); binaryWriter.Write(F2); } /// <inheritdoc /> public void Deserialize(BinaryReader binaryReader) { F1 = binaryReader.ReadString(); F2 = binaryReader.ReadInt32(); } public string F1 { set; get; } public int F2 { set; get; } /// <inheritdoc /> public bool Equals(FakeBinarySerialize other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return F1 == other.F1 && F2 == other.F2; } /// <inheritdoc /> public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((FakeBinarySerialize)obj); } /// <inheritdoc /> public override int GetHashCode() { unchecked { return ((F1 != null ? F1.GetHashCode() : 0) * 397) ^ F2; } } } [ContractTestCase] public void WriteUint32List() { "寫入uint列表,能夠讀取列表內容".Test(() => { // Arrange var memoryStream = new MemoryStream(); var binaryWriter = new BinaryWriter(memoryStream); var fakeBinarySerializeList = new List<uint>(); for (int i = 0; i < 100; i++) { fakeBinarySerializeList.Add((uint)i); } // Action binaryWriter.WriteUint32List(fakeBinarySerializeList); // Assert memoryStream.Seek(0, SeekOrigin.Begin); var binaryReader = new BinaryReader(memoryStream); var readList = binaryReader.ReadUint32List(); Equal(fakeBinarySerializeList, readList); }); } [ContractTestCase] public void WriteList() { "寫入列表內容,能夠讀取列表".Test(() => { // Arrange var memoryStream = new MemoryStream(); var binaryWriter = new BinaryWriter(memoryStream); var fakeBinarySerializeList = new List<FakeBinarySerialize>(); for (int i = 0; i < 100; i++) { fakeBinarySerializeList.Add(new FakeBinarySerialize() { F1 = i.ToString(), F2 = i, }); } // Action binaryWriter.WriteList(fakeBinarySerializeList); // Assert memoryStream.Seek(0, SeekOrigin.Begin); var binaryReader = new BinaryReader(memoryStream); var readList = binaryReader.ReadList<FakeBinarySerialize>(); Equal(fakeBinarySerializeList, readList); }); } private void Equal<T>(List<T> a, List<T> b) { Assert.AreEqual(a.Count, b.Count); for (int i = 0; i < a.Count; i++) { Assert.AreEqual(a[i], b[i]); } } [ContractTestCase] public void WriteStringList() { "寫入空字符串列表,能夠讀取空的列表".Test(() => { // Arrange var memoryStream = new MemoryStream(); var binaryWriter = new BinaryWriter(memoryStream); var head = "Font Data 1.0.0"; var length = 20; var test = (byte)0xF1; var stringList = new List<string>(); // Action binaryWriter.WriteHead(head, length); binaryWriter.Write(test); binaryWriter.WriteStringList(stringList); // Assert memoryStream.Seek(0, SeekOrigin.Begin); var binaryReader = new BinaryReader(memoryStream); var str = binaryReader.ReadHead(length); var b = binaryReader.ReadByte(); var readList = binaryReader.ReadStringList(); Assert.AreEqual(0, readList.Count); }); "寫入字符串列表,能夠讀取寫入的值".Test(() => { // Arrange var memoryStream = new MemoryStream(); var binaryWriter = new BinaryWriter(memoryStream); var head = "Font Data 1.0.0"; var length = 20; var test = (byte)0xF1; var stringList = new List<string>() { "lindexi","doubi" }; // Action binaryWriter.WriteHead(head, length); binaryWriter.Write(test); binaryWriter.WriteStringList(stringList); // Assert memoryStream.Seek(0, SeekOrigin.Begin); var binaryReader = new BinaryReader(memoryStream); var str = binaryReader.ReadHead(length); var b = binaryReader.ReadByte(); var readList = binaryReader.ReadStringList(); }); } [ContractTestCase] public void WriteHead() { "嘗試寫入長度小於指定長度的頭,能夠寫入和讀取".Test(() => { // Arrange var memoryStream = new MemoryStream(); var binaryWriter = new BinaryWriter(memoryStream); var head = "Font Data 1.0.0"; var length = 20; var test = (byte)0xF1; // Action binaryWriter.WriteHead(head, length); binaryWriter.Write(test); // Assert memoryStream.Seek(0, SeekOrigin.Begin); var binaryReader = new BinaryReader(memoryStream); var str = binaryReader.ReadHead(length); var b = binaryReader.ReadByte(); Assert.AreEqual(head, str); Assert.AreEqual(test, b); }); } }
上面代碼須要使用 CUnit 庫的支持,才能在單元測試裏面使用中文寫條件.net
<PackageReference Include="MSTestEnhancer" Version="1.6.0" />
我搭建了本身的博客 https://blog.lindexi.com/ 歡迎你們訪問,裏面有不少新的博客。只有在我看到博客寫成熟以後纔會放在csdn或博客園,可是一旦發佈了就再也不更新調試
若是在博客看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎你們加入code
若有不方便在博客評論的問題,能夠加我 QQ 2844808902 交流
本做品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、從新發布,但務必保留文章署名林德熙(包含連接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的做品務必以相同的許可發佈。若有任何疑問,請與我聯繫。