今天遇到有人提到结构体和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服务
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包来快速实现嵌套型结构体数据转换功能的更多相关文章

  1. CentOS6.8 RPM包安装快速zabbix22

    CentOS6.8 RPM包安装快速zabbix22 yum install -y epel-release # yum install -y httpd php php-devel mysql-se ...

  2. oracle函数、包、变量的定义和使用、重点”结构体和数组”

    函数 实例1:输入雇员的姓名,返回该雇员的年薪 create function fun1(spName varchar2) ,); begin +nvl(comm,) into yearSal fro ...

  3. C数组&结构体&联合体快速初始化

    背景 C89标准规定初始化语句的元素以固定顺序出现,该顺序即待初始化数组或结构体元素的定义顺序. C99标准新增指定初始化(Designated Initializer),即可按照任意顺序对数组某些元 ...

  4. 利用flask-sqlacodegen快速导入ORM表结构

    利用flask-sqlacodegen快速导入ORM表结构 友情提示:如果是使用pymysql请预先pip install 哦~ 这是window下使用virtualenv环境下执行的 Linux用户 ...

  5. TCP/IP各种数据包结构体

    下面这些TCP/IP数据包是我在进行Socket及Wipcap网络编程过程中曾经用到过的数据包结构体, 这些东西平时看起来不起眼,真正用到的时候就会觉得非常有用...... 以太帧头格式结构体,共14 ...

  6. mtools 是由MongoDB 官方工程师实现的一套工具集,可以很快速的日志查询分析、统计功能,此外还支持本地集群部署管理.

    mtools 是由MongoDB 官方工程师实现的一套工具集,可以很快速的日志查询分析.统计功能,此外还支持本地集群部署管理 https://www.cnblogs.com/littleatp/p/9 ...

  7. Go语言学习笔记(8)——包和结构体

    包 —— 每个可执行的应用程序必须包含一个主函数,它是执行的入口点.主函数应该存在main包中. 结构体: 通过 . 操作符访问结构体的各个成员! 1. 定义结构体类型person: type per ...

  8. struct2json -- C结构体与 JSON 快速互转库V1.0发布

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/zhutianlong/article/d ...

  9. C89,C99: C数组&结构体&联合体快速初始化

    1. 背景 C89标准规定初始化语句的元素以固定顺序出现,该顺序即待初始化数组或结构体元素的定义顺序. C99标准新增指定初始化(Designated Initializer),即可按照任意顺序对数组 ...

  10. Win10如何设置休眠选项(关于睡眠、休眠、快速启动这几个伪关机功能如何设置更适合笔记本电脑?)

    · Win10如何设置休眠选项(关于睡眠.休眠.快速启动这几个伪关机功能如何设置更适合笔记本电脑?) 应用场景 升级正式版win10以后,发现竟然没有休眠选项,从电源管理器里面也没有找到,有时候有些重 ...

随机推荐

  1. 基于 RocketMQ Prometheus Exporter 打造定制化 DevOps 平台

    简介: 本文将对 RocketMQ-Exporter 的设计实现做一个简单的介绍,读者可通过本文了解到 RocketMQ-Exporter 的实现过程,以及通过 RocketMQ-Exporter 来 ...

  2. Log4j漏洞不仅仅是修复,更需要构建有效预警机制

    ​简介:软件的漏洞有时不可避免,根据Gartner的相关统计,到 2025 年,30% 的关键信息基础设施组织将遇到安全漏洞.日志服务SLS,可帮助快速部署一个预警机制,使得漏洞被利用时可以快速发现并 ...

  3. PyFlink 教程(三):PyFlink DataStream API - state & timer

    简介: 介绍如何在 Python DataStream API 中使用 state & timer 功能. 一.背景 Flink 1.13 已于近期正式发布,超过 200 名贡献者参与了 Fl ...

  4. LlamaIndex 常见问题解答(FAQ)

    提示:如果您尚未完成,请安装 LlamaIndex 并完成起步教程.遇到不熟悉的术语时,请参考高层次概念部分. 在这个章节中,我们将从您为起步示例编写的代码开始,展示您可能希望针对不同应用场景对其进行 ...

  5. [FAQ] Golang error strings should not be capitalized or end with punctuation

    当我们在 Golang 中使用 errors.New("Aaa.") 形式返回 error 信息时,文字内容不应该以大写字母开头或者标点符号结尾. 所以这样是可以的 errors. ...

  6. WPF 简单判断主线程界面是否卡顿的方法

    本文来告诉大家如何使用简单的代码判断当前的软件的 UI 线程或界面是否卡顿 在后台线程调用如下代码即可用来判断是否卡顿 private static async Task<bool> Ch ...

  7. Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/xxx". at createRouterError 的说明和解决

    错误说明 Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: & ...

  8. Ubuntu 20.04版本安装k8s控制节点与控制节点升级

    一.环境配置 服务器配置:2核4G IP:192.168.10.23 主机名:master4将改主机加入此 集群 # 1.修改主机名 hostnamectl set-hostname master4 ...

  9. STM32 USART串口通信

    一.介绍 通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换.USART利用分数波特率发生器提供宽范围的波特率选择.它支持同步单向 ...

  10. Codeforces 题解集

    Codeforces Round 940 (Div. 2) and CodeCraft-23 Codeforces Round 940 (Div. 2) and CodeCraft-23 (A-E)