今天遇到有人提到結構體和byte陣列互轉的問題,我就順便拿來水一篇。這是一個冷門的問題,估計使用的人不多。既然有需求,應該就有使用場景,那就順便整一波。
為了達到效果,結構體、複雜結構體巢狀等都能實現轉換,我就順便做了個包更新來提供使用和下面的說明。
首先引入nuget包 Wesky.Net.OpenTools 的最新版
新建幾個結構體做實驗。結構體結構如下所示,做四個層級的巢狀,包括陣列、基礎型別、結構體陣列和巢狀等。
使用方式:
對結構體屬性進行賦值等操作,模擬一個我們要做的物件資料。
例項化一個轉換器
轉換器選擇方式有兩種,一種針對基礎型別的操作,用Marshal自帶的方法進行實現。另一種為複雜型別的轉換實現。此處主要演示第二種(上面結構體會自動選擇第二種轉換器)
轉換器選擇內部實現原始碼如下:
1 /// <summary>
2 /// 提供結構體轉換器的工廠類。
3 /// Provides a factory class for structure converters.
4 /// </summary>
5 public class StructConvertFactory
6 {
7 /// <summary>
8 /// 根據結構體型別的複雜性選擇合適的轉換器。
9 /// Selects an appropriate converter based on the complexity of the structure type.
10 /// </summary>
11 /// <typeparam name="T">要為其建立轉換器的結構體型別。</typeparam>
12 /// <returns>返回符合結構體型別特性的轉換器例項。</returns>
13 /// <remarks>
14 /// 如果結構體包含複雜欄位,則返回一個基於反射的轉換器,否則返回一個基於記憶體操作的轉換器。
15 /// If the structure contains complex fields, a reflection-based converter is returned; otherwise, a memory operation-based converter is provided.
16 /// </remarks>
17 public static IStructConvert CreateConvertor<T>() where T : struct
18 {
19 // 判斷結構體型別T是否包含複雜欄位
20 if (HasComplexFields(typeof(T)))
21 {
22 // 返回反射方式實現的結構體轉換器
23 return new StructConvert();
24 }
25 else
26 {
27 // 返回Marshal自帶的操作方式實現的結構體轉換器
28 return new MarshalConvert();
29 }
30 }
31
32 /// <summary>
33 /// 驗證指定型別的欄位是否包含複雜型別。
34 /// Verifies whether the fields of the specified type contain complex types.
35 /// </summary>
36 /// <param name="type">要檢查的型別。</param>
37 /// <returns>如果包含複雜型別欄位,則返回true;否則返回false。</returns>
38 /// <remarks>
39 /// 複雜型別包括陣列、類以及非基本的值型別(如結構體),但不包括decimal。
40 /// Complex types include arrays, classes, and non-primitive value types such as structures, but exclude decimal.
41 /// </remarks>
42 private static bool HasComplexFields(Type type)
43 {
44 foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
45 {
46 if (field.FieldType.IsArray || field.FieldType.IsClass ||
47 (field.FieldType.IsValueType && !field.FieldType.IsPrimitive &&
48 field.FieldType != typeof(decimal)))
49 {
50 return true;
51 }
52 }
53 return false;
54 }
55 }
轉換器都繼承自IStructConvert介面,IStructConvert介面定義如下
1 /// <summary>
2 /// IStructConvert 介面,提供結構體與位元組陣列之間的序列化和反序列化功能。
3 /// IStructConvert interface, providing serialization and deserialization functionality between structures and byte arrays.
4 /// </summary>
5 public interface IStructConvert
6 {
7 /// <summary>
8 /// 將位元組陣列反序列化為結構體。
9 /// Deserializes a byte array into a structure.
10 /// </summary>
11 /// <typeparam name="T">結構體的型別。</typeparam>
12 /// <param name="data">包含結構體資料的位元組陣列。</param>
13 /// <returns>反序列化後的結構體例項。</returns>
14 byte[] StructToBytes<T>(T structure) where T : struct;
15
16 /// <summary>
17 /// 將結構體例項轉換為位元組陣列。
18 /// Converts a structure instance into a byte array.
19 /// </summary>
20 /// <typeparam name="T">要轉換的結構體型別,必須是值型別。</typeparam>
21 /// <param name="structure">要轉換的結構體例項。</param>
22 /// <returns>表示結構體資料的位元組陣列。</returns>
23 T BytesToStruct<T>(byte[] data) where T : struct;
24 }
所以下面我們可以直接呼叫轉換器的這兩個方法來實現資料的轉換:
設定斷點,執行程式。監視到byte陣列的data資料有77個元素
繼續監控陣列資料轉換回來的資料,可以對比到物件的資料和上面定義的內容是一致的,說明資料轉換成功。
其他核心程式碼——MarshalConvert類轉換器程式碼:
1 /// <summary>
2 /// 實現IStructConvert介面,提供結構體與位元組陣列間的基本轉換功能。
3 /// Implements the IStructConvert interface to provide conversion between structures and byte arrays.
4 /// </summary>
5 public class MarshalConvert : IStructConvert
6 {
7 /// <summary>
8 /// 將位元組陣列轉換為指定型別的結構體例項。
9 /// Converts a byte array into an instance of the specified type of structure.
10 /// </summary>
11 /// <typeparam name="T">要轉換的結構體型別,必須是值型別。</typeparam>
12 /// <param name="data">包含結構體資料的位元組陣列。</param>
13 /// <returns>轉換後的結構體例項。</returns>
14 public T BytesToStruct<T>(byte[] data) where T : struct
15 {
16 T structure;
17 // 計算結構體型別T的記憶體大小
18 // Calculate the memory size of the structure type T
19 int size = Marshal.SizeOf(typeof(T));
20 // 分配相應大小的記憶體緩衝區
21 // Allocate a memory buffer of the appropriate size
22 IntPtr buffer = Marshal.AllocHGlobal(size);
23 try
24 {
25 // 將位元組陣列複製到分配的記憶體中
26 // Copy the byte array to the allocated memory
27 Marshal.Copy(data, 0, buffer, size);
28 // 將記憶體緩衝區轉換為指定的結構體
29 // Convert the memory buffer to the specified structure
30 structure = Marshal.PtrToStructure<T>(buffer);
31 }
32 finally
33 {
34 // 釋放記憶體緩衝區
35 // Free the memory buffer
36 Marshal.FreeHGlobal(buffer);
37 }
38 return structure;
39 }
40
41 /// <summary>
42 /// 將結構體例項轉換為位元組陣列。
43 /// Converts a structure instance into a byte array.
44 /// </summary>
45 /// <typeparam name="T">要轉換的結構體型別,必須是值型別。</typeparam>
46 /// <param name="structure">要轉換的結構體例項。</param>
47 /// <returns>表示結構體資料的位元組陣列。</returns>
48 public byte[] StructToBytes<T>(T structure) where T : struct
49 {
50 // 計算結構體例項的記憶體大小
51 // Calculate the memory size of the structure instance
52 int size = Marshal.SizeOf(structure);
53 byte[] array = new byte[size];
54 // 分配相應大小的記憶體緩衝區
55 // Allocate a memory buffer of the appropriate size
56 IntPtr buffer = Marshal.AllocHGlobal(size);
57 try
58 {
59 // 將結構體例項複製到記憶體緩衝區
60 // Copy the structure instance to the memory buffer
61 Marshal.StructureToPtr(structure, buffer, false);
62 // 將記憶體緩衝區的資料複製到位元組陣列
63 // Copy the data from the memory buffer to the byte array
64 Marshal.Copy(buffer, array, 0, size);
65 }
66 finally
67 {
68 // 釋放記憶體緩衝區
69 // Free the memory buffer
70 Marshal.FreeHGlobal(buffer);
71 }
72 return array;
73 }
74 }
如果以上內容對你有幫助,歡迎點贊、轉發、在看和關注我的個人公眾號:【Dotnet Dancer】
如果需要以上演示程式碼,可以在公眾號【Dotnet Dancer】後臺回覆“結構體轉換”進行獲取。
OpenTools系列文章快捷連結【新版本完全相容舊版本,不需要更新任何程式碼均可使用】:
1.0.13版本
快速實現.NET(.net framework/.net core+)動態訪問webservice服務
https://mp.weixin.qq.com/s/KoLpaBaYX7_ETP0dfgQfyw
1.0.11版本
如何一行C#程式碼實現解析型別的Summary註釋(可用於資料字典快速生成)
https://mp.weixin.qq.com/s/CWqubRRMoYVQIQJSyjIUXg
1.0.10版本:
C#/.NET一行程式碼把實體類型別轉換為Json資料字串
https://mp.weixin.qq.com/s/nVcURD0lf5-AQOVzwHqcxw
1.0.8版本:
上位機和工控必備!用.NET快速搞定Modbus通訊的方法
https://mp.weixin.qq.com/s/Yq6kuXzFglHfNUqrHcQO9w
1.0.7版本:
大揭秘!.Net如何在5分鐘內快速實現物聯網掃碼器通用掃碼功能?
https://mp.weixin.qq.com/s/-5VuLAS6HlElgDQXRY9-BQ
1.0.6版本:
.NET實現獲取NTP伺服器時間並同步(附帶Windows系統啟用NTP服務功能)
https://mp.weixin.qq.com/s/vMW0vYC-D9z0Dp6HFSBqyg
1.0.5版本:
C#使用P/Invoke來實現登錄檔的增刪改查功能
https://mp.weixin.qq.com/s/LpsjBhDDzkwyLU_tIpF-lg
1.0.3版本:
C#實現圖片轉Base64字串,以及base64字串在Markdown檔案內復原的演示
https://mp.weixin.qq.com/s/n9VtTCIiVUbHJk7OfoCcvA
1.0.2版本:
C#實現Ping遠端主機功能(支援IP和域名)
https://mp.weixin.qq.com/s/d-2HcIM1KaLo-FrrTLkwEw
1.0.1版本:
開始開源專案OpenTools的創作(第一個功能:AES加密解密)
https://mp.weixin.qq.com/s/78TA-mst459AuvAHwQViqQ
【備註】包版本完全開源,並且沒有任何第三方依賴。使用.net framework 4.6+、任意其他跨平臺.net版本環境,均可直接引用。