C#结构体与字节数组互转实现方法

咖啡半糖
咖啡半糖
发布时间:2026-10-13| 5人浏览过
原创

在C#开发中,特别是在进行Socket网络编程时,经常需要将结构体数据序列化为字节数组进行发送,或接收到的字节数组反序列化为结构体。本文详细讲解了利用 System.Runtime.InteropServices.Marshal API 实现这两种转换的核心方法、关键参数配置及安全注意事项。

使用场景与准备条件

在处理底层网络协议、文件格式解析或进程间通信时,直接操作字节流往往比序列化对象更高效。要实现结构体与字节数组的互转,需满足以下条件:

  • 命名空间引用:必须引入 using System.Runtime.InteropServices;。
  • 内存布局控制:结构体必须明确指定内存排列方式(如紧凑排列),确保在网络端和接收端的内存模型一致。
  • 环境配置:部分操作(如获取结构体大小)需要启用编译器的“允许不安全代码”选项,或在特定API调用时使用非安全上下文。

第一步:定义结构体及其内存布局

为了让二进制序列化正确工作,必须使用 [StructLayout] 特性修饰结构体。这告诉编译器如何分配字段在内存中的位置。

[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct StructDemo
{
    public byte a;
    public byte c;
    
    // 注意:定长数组需要指定 SizeConst
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] b;
    
    public byte d;
    public int e;
}

关键参数说明:

  • LayoutKind.Sequential:按字段声明顺序排列。
  • Pack = 1:强制按1字节对齐,去除默认的成员对齐填充,确保结构体大小固定且紧凑。
  • [MarshalAs]:用于标记数组成员,指定其长度为固定值(SizeConst = 3),否则 Marshal 无法正确计算偏移量。

第二步:结构体转为字节数组 (StructToBytes)

此过程涉及内存分配、数据拷贝和资源释放。核心逻辑是将托管对象指针转换为非托管内存块,然后复制内容到字节数组。

public static byte[] StructToBytes(object structObj, int size)
{
    byte[] bytes = new byte[size];
    // 1. 分配非托管内存
    IntPtr structPtr = Marshal.AllocHGlobal(size);
    
    try
    {
        // 2. 将结构体复制到非托管内存 (false 表示不删除原指针内容)
        Marshal.StructureToPtr(structObj, structPtr, false);
        
        // 3. 从非托管内存拷贝到托管字节数组
        Marshal.Copy(structPtr, bytes, 0, size);
    }
    finally
    {
        // 4. 务必释放非托管内存,防止内存泄漏
        Marshal.FreeHGlobal(structPtr);
    }
    
    return bytes;
}

操作要点:

  • 先通过 Marshal.SizeOf(structObj) 确定所需内存大小。
  • 使用 try...finally 包裹内存释放逻辑,即使中间发生异常也能确保资源回收。

第三步:字节数组转为结构体 (ByteToStruct)

反向操作需要从字节数组读取数据,映射回目标类型的内存布局中。

public static object ByteToStruct(byte[] bytes, Type type)
{
    // 1. 校验长度,确保数据完整
    int size = Marshal.SizeOf(type);
    if (size > bytes.Length)
    {
        return null; // 数据不足,返回null或抛出异常
    }
    
    IntPtr structPtr = IntPtr.Zero;
    try
    {
        // 2. 分配足够大小的非托管内存
        structPtr = Marshal.AllocHGlobal(size);
        
        // 3. 将字节数组内容拷贝到非托管内存
        Marshal.Copy(bytes, 0, structPtr, size);
        
        // 4. 将非托管内存转换为指定类型的结构体
        object obj = Marshal.PtrToStructure(structPtr, type);
        return obj;
    }
    finally
    {
        // 5. 释放内存
        if (structPtr != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(structPtr);
        }
    }
}

完成判断与排错指南

验证转换是否成功,可通过以下步骤确认:

  1. 长度一致性:输出的字节数组长度应严格等于结构体定义的内存大小(可通过 sizeof 或调试查看)。
  2. 字段对应性:将生成的字节数组再次转换回结构体后,对比各字段值是否与原始输入一致。
  3. 常见错误排查:
  • 数据错位:检查 Pack 设置是否两端一致,或者数组是否定义了 SizeConst。
  • 内存泄漏:若在循环中频繁转换,务必检查 FreeHGlobal 是否被调用。
  • 索引越界:接收到的字节数组长度小于结构体预期大小时,Marshal.Copy 可能报错,需在入口处进行长度判断。

以上就是C#中结构体与字节数组互转实现的详细内容,更多关于C#网络编程数据序列化资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。