使用Wesky.Net.OpenTools包來快速實現巢狀型結構體資料轉換功能

WeskyNet發表於2024-06-12
今天遇到有人提到結構體和byte陣列互轉的問題,我就順便拿來水一篇。這是一個冷門的問題,估計使用的人不多。既然有需求,應該就有使用場景,那就順便整一波。
為了達到效果,結構體、複雜結構體巢狀等都能實現轉換,我就順便做了個包更新來提供使用和下面的說明。
首先引入nuget包 Wesky.Net.OpenTools 的最新版
0
新建幾個結構體做實驗。結構體結構如下所示,做四個層級的巢狀,包括陣列、基礎型別、結構體陣列和巢狀等。
0
使用方式:
對結構體屬性進行賦值等操作,模擬一個我們要做的物件資料。
0
例項化一個轉換器
0
轉換器選擇方式有兩種,一種針對基礎型別的操作,用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 }

所以下面我們可以直接呼叫轉換器的這兩個方法來實現資料的轉換:
0
設定斷點,執行程式。監視到byte陣列的data資料有77個元素
0
繼續監控陣列資料轉換回來的資料,可以對比到物件的資料和上面定義的內容是一致的,說明資料轉換成功。
0
其他核心程式碼——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版本環境,均可直接引用。

相關文章