前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#嵌套型结构体数据的转换

C#嵌套型结构体数据的转换

作者头像
Wesky
发布2024-08-13 19:21:28
800
发布2024-08-13 19:21:28
举报
文章被收录于专栏:Dotnet Dancer

前言:今天遇到有人提到结构体和byte数组互转的问题,我就顺便拿来水一篇。这是一个冷门的问题,估计使用的人不多。既然有需求,应该就有使用场景,那就顺便整一波。

为了达到效果,结构体、复杂结构体嵌套等都能实现转换,我就顺便做了个包更新来提供使用和下面的说明。

首先引入nuget包 Wesky.Net.OpenTools 的最新版

新建几个结构体做实验。结构体结构如下所示,做四个层级的嵌套,包括数组、基础类型、结构体数组和嵌套等。

使用方式:

对结构体属性进行赋值等操作,模拟一个我们要做的对象数据。

实例化一个转换器

转换器选择方式有两种,一种针对基础类型的操作,用Marshal自带的方法进行实现。另一种为复杂类型的转换实现。此处主要演示第二种(上面结构体会自动选择第二种转换器)

转换器选择内部实现源码如下:

代码语言:javascript
复制
/// <summary>
/// 提供结构体转换器的工厂类。
/// Provides a factory class for structure converters.
/// </summary>
public class StructConvertFactory
{
    /// <summary>
    /// 根据结构体类型的复杂性选择合适的转换器。
    /// Selects an appropriate converter based on the complexity of the structure type.
    /// </summary>
    /// <typeparam name="T">要为其创建转换器的结构体类型。</typeparam>
    /// <returns>返回符合结构体类型特性的转换器实例。</returns>
    /// <remarks>
    /// 如果结构体包含复杂字段,则返回一个基于反射的转换器,否则返回一个基于内存操作的转换器。
    /// If the structure contains complex fields, a reflection-based converter is returned; otherwise, a memory operation-based converter is provided.
    /// </remarks>
    public static IStructConvert CreateConvertor<T>() where T : struct
    {
        // 判断结构体类型T是否包含复杂字段
        if (HasComplexFields(typeof(T)))
        {
            // 返回反射方式实现的结构体转换器
            return new StructConvert();
        }
        else
        {
            // 返回Marshal自带的操作方式实现的结构体转换器
            return new MarshalConvert();
        }
    }

    /// <summary>
    /// 验证指定类型的字段是否包含复杂类型。
    /// Verifies whether the fields of the specified type contain complex types.
    /// </summary>
    /// <param name="type">要检查的类型。</param>
    /// <returns>如果包含复杂类型字段,则返回true;否则返回false。</returns>
    /// <remarks>
    /// 复杂类型包括数组、类以及非基本的值类型(如结构体),但不包括decimal。
    /// Complex types include arrays, classes, and non-primitive value types such as structures, but exclude decimal.
    /// </remarks>
    private static bool HasComplexFields(Type type)
    {
        foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
        {
            if (field.FieldType.IsArray || field.FieldType.IsClass ||
                (field.FieldType.IsValueType && !field.FieldType.IsPrimitive &&
                 field.FieldType != typeof(decimal)))
            {
                return true;
            }
        }
        return false;
    }
}

转换器都继承自IStructConvert接口,IStructConvert接口定义如下

代码语言:javascript
复制
/// <summary>
/// IStructConvert 接口,提供结构体与字节数组之间的序列化和反序列化功能。
/// IStructConvert interface, providing serialization and deserialization functionality between structures and byte arrays.
/// </summary>
public interface IStructConvert
{
    /// <summary>
    /// 将字节数组反序列化为结构体。
    /// Deserializes a byte array into a structure.
    /// </summary>
    /// <typeparam name="T">结构体的类型。</typeparam>
    /// <param name="data">包含结构体数据的字节数组。</param>
    /// <returns>反序列化后的结构体实例。</returns>
    byte[] StructToBytes<T>(T structure) where T : struct;

    /// <summary>
    /// 将结构体实例转换为字节数组。
    /// Converts a structure instance into a byte array.
    /// </summary>
    /// <typeparam name="T">要转换的结构体类型,必须是值类型。</typeparam>
    /// <param name="structure">要转换的结构体实例。</param>
    /// <returns>表示结构体数据的字节数组。</returns>
    T BytesToStruct<T>(byte[] data) where T : struct;
}

所以下面我们可以直接调用转换器的这两个方法来实现数据的转换:

设置断点,执行程序。监视到byte数组的data数据有77个元素

继续监控数组数据转换回来的数据,可以对比到对象的数据和上面定义的内容是一致的,说明数据转换成功。

其他核心代码——MarshalConvert类转换器代码:

代码语言:javascript
复制
  /// <summary>
  /// 实现IStructConvert接口,提供结构体与字节数组间的基本转换功能。
  /// Implements the IStructConvert interface to provide conversion between structures and byte arrays.
  /// </summary>
  public class MarshalConvert : IStructConvert
  {
      /// <summary>
      /// 将字节数组转换为指定类型的结构体实例。
      /// Converts a byte array into an instance of the specified type of structure.
      /// </summary>
      /// <typeparam name="T">要转换的结构体类型,必须是值类型。</typeparam>
      /// <param name="data">包含结构体数据的字节数组。</param>
      /// <returns>转换后的结构体实例。</returns>
      public T BytesToStruct<T>(byte[] data) where T : struct
      {
          T structure;
          // 计算结构体类型T的内存大小
          // Calculate the memory size of the structure type T
          int size = Marshal.SizeOf(typeof(T));
          // 分配相应大小的内存缓冲区
          // Allocate a memory buffer of the appropriate size
          IntPtr buffer = Marshal.AllocHGlobal(size);
          try
          {
              // 将字节数组复制到分配的内存中
              // Copy the byte array to the allocated memory
              Marshal.Copy(data, 0, buffer, size);
              // 将内存缓冲区转换为指定的结构体
              // Convert the memory buffer to the specified structure
              structure = Marshal.PtrToStructure<T>(buffer);
          }
          finally
          {
              // 释放内存缓冲区
              // Free the memory buffer
              Marshal.FreeHGlobal(buffer);
          }
          return structure;
      }

      /// <summary>
      /// 将结构体实例转换为字节数组。
      /// Converts a structure instance into a byte array.
      /// </summary>
      /// <typeparam name="T">要转换的结构体类型,必须是值类型。</typeparam>
      /// <param name="structure">要转换的结构体实例。</param>
      /// <returns>表示结构体数据的字节数组。</returns>
      public byte[] StructToBytes<T>(T structure) where T : struct
      {
          // 计算结构体实例的内存大小
          // Calculate the memory size of the structure instance
          int size = Marshal.SizeOf(structure);
          byte[] array = new byte[size];
          // 分配相应大小的内存缓冲区
          // Allocate a memory buffer of the appropriate size
          IntPtr buffer = Marshal.AllocHGlobal(size);
          try
          {
              // 将结构体实例复制到内存缓冲区
              // Copy the structure instance to the memory buffer
              Marshal.StructureToPtr(structure, buffer, false);
              // 将内存缓冲区的数据复制到字节数组
              // Copy the data from the memory buffer to the byte array
              Marshal.Copy(buffer, array, 0, size);
          }
          finally
          {
              // 释放内存缓冲区
              // Free the memory buffer
              Marshal.FreeHGlobal(buffer);
          }
          return array;
      }
  }

如果以上内容对你有帮助,欢迎点赞、转发、在看和关注我的个人公众号:【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

【备注】包版本完全开源,并且没有任何第三方依赖。使用.net framework 4.6+、任意其他跨平台.net版本环境,均可直接引用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Dotnet Dancer 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档