使用Wesky.Net.OpenTools包来快速实现嵌套型结构体数据转换功能

wesky,net,opentools · 浏览次数 : 0

小编点评

结构体与字节数组的互转是软件开发过程中常见的一个需求,在处理这类问题时,我们通常会考虑使用数据结构的相关库来进行转换。在本文中,我们介绍了一种结构体转换工厂的实现,该工厂可以根据结构体的复杂性选择合适的转换器来实现字节数组向结构体的转换或结构体向字节数组的转换。 首先,我们来了解一下结构体和转换器的相关概念。结构体是一种复合数据类型,它由多个基本数据类型或数组组成,每个组成部分称为一个成员。而转换器是一个实现了IStructConvert接口的对象,该接口提供了一种方法来完成结构体和字节数组之间的序列化和反序列化工作。 在结构体和字节数组之间的转换中,我们需要考虑到结构的复杂性和字段类型。如果结构很简单,只包含一些简单的字段,比如int、float等,那么我们就可以使用 Marshal 自带的转换器来完成转换。但如果结构体比较复杂,包含了诸如数组、类或者结构体等其他类型时,我们就需要使用反射来得知各个成员的类型信息,并通过反射来创建一个真正的转换器来进行转换。 具体来说,我们可以使用以下几种方法来进行结构体和字节数组之间的转换: 1. 使用 Marshal 自带的转换器来完成最基础的转换。 2. 如果结构体中包含了复杂字段,比如数组或类,那么我们需要使用反射来获取这些成员的类型信息,然后根据这些信息生成一个自定义的转换器。 3. 在某些情况下,我们需要编写一个定制的转换器来处理特定的数据类型或者特定场景下的转换需求。 在实现结构体的转换时,我们需要注意以下几个关键问题: 1. 确定数据结构的大小,以计算出需要的内存缓冲区的大小。 2. 正确地分配和管理内存,以避免出现内存泄漏或其他异常情况的发生。 3. 注意字段的类型安全,确保在进行转换时不会发生类型转换错误。 综上所述,结构体和字节数组的互转是一个复杂的问题,需要根据具体情况来选择合适的转换方法和工具,以确保转换的正确性和性能。在实际应用中,这种操作可能需要频繁地迭代和优化,以达到最佳的使用体验和性能表现。

正文

今天遇到有人提到结构体和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服务
1.0.11版本
如何一行C#代码实现解析类型的Summary注释(可用于数据字典快速生成)
1.0.10版本:
C#/.NET一行代码把实体类类型转换为Json数据字符串
1.0.8版本:
上位机和工控必备!用.NET快速搞定Modbus通信的方法
1.0.7版本:
大揭秘!.Net如何在5分钟内快速实现物联网扫码器通用扫码功能?
1.0.6版本:
.NET实现获取NTP服务器时间并同步(附带Windows系统启用NTP服务功能)
1.0.5版本:
C#使用P/Invoke来实现注册表的增删改查功能
1.0.3版本:
C#实现图片转Base64字符串,以及base64字符串在Markdown文件内复原的演示
1.0.2版本:
​C#实现Ping远程主机功能(支持IP和域名)
1.0.1版本:
开始开源项目OpenTools的创作(第一个功能:AES加密解密)
 
【备注】包版本完全开源,并且没有任何第三方依赖。使用.net framework 4.6+、任意其他跨平台.net版本环境,均可直接引用。

与使用Wesky.Net.OpenTools包来快速实现嵌套型结构体数据转换功能相似的内容:

使用Wesky.Net.OpenTools包来快速实现嵌套型结构体数据转换功能

今天遇到有人提到结构体和byte数组互转的问题,我就顺便拿来水一篇。这是一个冷门的问题,估计使用的人不多。既然有需求,应该就有使用场景,那就顺便整一波。 为了达到效果,结构体、复杂结构体嵌套等都能实现转换,我就顺便做了个包更新来提供使用和下面的说明。 首先引入nuget包 Wesky.Net.Ope

使用Wesky.Net.Opentools库,一行代码实现实体类类型转换为Json格式字符串

安装1.0.10以及以上版本的 Wesky.Net.OpenTools 包 包内,该功能的核心代码如下: 自定义属性: 实体类JSON模式生成器: 使用方式:引用上面的1.0.10版本或以上的包。如果实体类有特殊需求,例如映射为其他名称,可以用OpenJson属性来实现。实体类对象案例如下: 上面实

.NET使用P/Invoke来实现注册表的增、删、改、查功能

注册表可以用来进行存储一些程序的信息,例如用户的权限、或者某些值等,可以根据个人需要进行存储和删减。 当前注册表主目录: 引用包 Wesky.Net.OpenTools 1.0.5或者以上版本 操作演示: 创建注册表项 设置注册表值 读取注册表值 删除注册表值 删除注册表项 操作演示代码 IRegi

使用Wesky.Net.Opentools库,一行代码实现自动解析实体类summary注释信息(可用于数据实体文档的快速实现)

使用前,需要对你的项目勾选输出api文档文件。 引用Wesky.Net.OpenTools包,保持1.0.11版本或以上。 为了方便,我直接在昨天的演示基础上,继续给实体类添加注释。 昨天的演示文章可参考: C#/.NET一行代码把实体类类型转换为Json数据字符串 https://mp.weixi

上位机开发福利!快速掌握.NET中的Modbus通信

安装nuget包 Wesky.Net.OpenTools 1.0.8或以上版本。支持.net framework 4.6以上版本,以及所有.net core以及以上版本引用。 开发一个简单的Winform界面,用来测试使用。如需该winform的demo,可以在公众号【Dotnet Dancer】后

使用C#/.NET解析Wiki百科数据实现获取历史上的今天

创建一个webapi项目做测试使用。 创建新控制器,搭建一个基础框架,包括获取当天日期、wiki的请求地址等 创建一个Http请求帮助类以及方法,用于获取指定URL的信息 使用http请求访问指定url,先运行一下,看看返回的内容。内容如图右边所示,实际上是一个Json数据。我们主要解析 大事记 部

Python使用.NET开发的类库来提高你的程序执行效率

Python由于本身的特性原因,执行程序期间可能效率并不是很理想。在某些需要自己提高一些代码的执行效率的时候,可以考虑使用C#、C++、Rust等语言开发的库来提高python本身的执行效率。接下来,我演示一种使用.NET平台开发的类库,来演示一下Python访问.NET类库的操作实现。类库演示包括

WPF使用事件聚合器,实现任意页面跨页通信

前言:最近几天有好几个小伙伴玩WPF,遇到不同页面,不知道要怎么传递消息。于是,我今天就来演示一个事件聚合器的玩法,采用prism框架来实现。作为福利,内容附带了主页面打开对话框时候直接通过参数传递消息的一个小例子,具体请自行围观。 以下内容,创建wpf项目以及引用prism和实现依赖注入等细节,可

谁说.net core不好动态访问webservice?看这篇文章,C#快速实现动态访问webservice,兼容.net framework和.net core+

前言:访问webservice,大多数人都是用服务引用的方式,但是这种方式比较麻烦,例如遇到服务更新了,你还需要手动更新你的服务引用,再重新发布,很麻烦。或者已有的一些例子,至少我看到的很多案例,动态访问也只能止步于使用.net framework环境,没看到有啥.net core上面动态访问的案例

一款WPF的精简版MVVM框架——stylet框架的初体验(包括MVVM绑定、依赖注入等操作)

今天偶然知道一款叫做stylet的MVVM框架,挺小巧的,特别是它的命令触发方式,简单粗暴,让人感觉很巴适,现在我做一个简单的demo来顺便来分享给大家。 本地创建一个WPF项目,此处我使用.NET 8来创建。然后引用stylet最新的nuget包。 然后删掉App.xaml里面自带的启动项 删掉以