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


    今天遇到有人提到结构体和byte数组互转的问题,我就顺便拿来水一篇。这是一个冷门的问题,估计使用的人不多。既然有需求,应该就有使用场景,那就顺便整一波。
    为了达到效果,结构体、复杂结构体嵌套等都能实现转换,我就顺便做了个包更新来提供使用和下面的说明。
    首先引入nuget包 Wesky.Net.OpenTools 的最新版
    0
     
    新建几个结构体做实验。结构体结构如下所示,做四个层级的嵌套,包括数组、基础类型、结构体数组和嵌套等。
    0
     
    使用方式:
    对结构体属性进行赋值等操作,模拟一个我们要做的对象数据。
    0
    实例化一个转换器
    0
    转换器选择方式有两种,一种针对基础类型的操作,用Marshal自带的方法进行实现。另一种为复杂类型的转换实现。此处主要演示第二种(上面结构体会自动选择第二种转换器)
    转换器选择内部实现源码如下:
    复制代码
     1 /// 
     2 /// 提供结构体转换器的工厂类。
     3 /// Provides a factory class for structure converters.
     4 /// 
     5 public class StructConvertFactory
     6 {
     7     /// 
     8     /// 根据结构体类型的复杂性选择合适的转换器。
     9     /// Selects an appropriate converter based on the complexity of the structure type.
    10     /// 
    11     /// 要为其创建转换器的结构体类型。
    12     /// 返回符合结构体类型特性的转换器实例。
    13     /// 
    14     /// 如果结构体包含复杂字段,则返回一个基于反射的转换器,否则返回一个基于内存操作的转换器。
    15     /// If the structure contains complex fields, a reflection-based converter is returned; otherwise, a memory operation-based converter is provided.
    16     /// 
    17     public static IStructConvert CreateConvertor() 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     /// 
    33     /// 验证指定类型的字段是否包含复杂类型。
    34     /// Verifies whether the fields of the specified type contain complex types.
    35     /// 
    36     /// 要检查的类型。
    37     /// 如果包含复杂类型字段,则返回true;否则返回false。
    38     /// 
    39     /// 复杂类型包括数组、类以及非基本的值类型(如结构体),但不包括decimal。
    40     /// Complex types include arrays, classes, and non-primitive value types such as structures, but exclude decimal.
    41     /// 
    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 /// 
     2 /// IStructConvert 接口,提供结构体与字节数组之间的序列化和反序列化功能。
     3 /// IStructConvert interface, providing serialization and deserialization functionality between structures and byte arrays.
     4 /// 
     5 public interface IStructConvert
     6 {
     7     /// 
     8     /// 将字节数组反序列化为结构体。
     9     /// Deserializes a byte array into a structure.
    10     /// 
    11     /// 结构体的类型。
    12     /// 包含结构体数据的字节数组。
    13     /// 反序列化后的结构体实例。
    14     byte[] StructToBytes(T structure) where T : struct;
    15 
    16     /// 
    17     /// 将结构体实例转换为字节数组。
    18     /// Converts a structure instance into a byte array.
    19     /// 
    20     /// 要转换的结构体类型,必须是值类型。
    21     /// 要转换的结构体实例。
    22     /// 表示结构体数据的字节数组。
    23     T BytesToStruct(byte[] data) where T : struct;
    24 }
    复制代码

     

    所以下面我们可以直接调用转换器的这两个方法来实现数据的转换:
    0
    设置断点,执行程序。监视到byte数组的data数据有77个元素
    0
    继续监控数组数据转换回来的数据,可以对比到对象的数据和上面定义的内容是一致的,说明数据转换成功。
    0
    其他核心代码——MarshalConvert类转换器代码:
    复制代码
     1   /// 
     2   /// 实现IStructConvert接口,提供结构体与字节数组间的基本转换功能。
     3   /// Implements the IStructConvert interface to provide conversion between structures and byte arrays.
     4   /// 
     5   public class MarshalConvert : IStructConvert
     6   {
     7       /// 
     8       /// 将字节数组转换为指定类型的结构体实例。
     9       /// Converts a byte array into an instance of the specified type of structure.
    10       /// 
    11       /// 要转换的结构体类型,必须是值类型。
    12       /// 包含结构体数据的字节数组。
    13       /// 转换后的结构体实例。
    14       public T BytesToStruct(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(buffer);
    31           }
    32           finally
    33           {
    34               // 释放内存缓冲区
    35               // Free the memory buffer
    36               Marshal.FreeHGlobal(buffer);
    37           }
    38           return structure;
    39       }
    40 
    41       /// 
    42       /// 将结构体实例转换为字节数组。
    43       /// Converts a structure instance into a byte array.
    44       /// 
    45       /// 要转换的结构体类型,必须是值类型。
    46       /// 要转换的结构体实例。
    47       /// 表示结构体数据的字节数组。
    48       public byte[] StructToBytes(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版本环境,均可直接引用。
  • 相关阅读:
    redis archive github
    前端工程化之模块化基础
    音视频转换软件Permute mac中文板特点介绍
    MySQL之InnoDB浅析
    【Maven】maven安装、IDEA创建maven的web项目、添加依赖、集成Tomcat
    【网络安全】web渗透漏洞靶场收集
    在数据链路层扩展以太网
    TDengine 入门教程①——时序数据库简单介绍和产品对比
    vscode pandas无法使用
    openssl lib includefor windows
  • 原文地址:https://www.cnblogs.com/weskynet/p/18244720