C# 结构体和字节数组的转换

时间:2023-01-11 23:43:51

转自百度文库里的文章,本想下载下来的,但是分值实在太高,所以直接把代码抄了一遍,顺便当做练习。另外,还有一些东西不是很明白。

本文主要起源于项目从C++转到C#时碰到的问题,即套接字发送信息时的类型转换。

在C++中,套接字发送和接收的类型为字符数组char[],而char[]与结构体struct可以直接进行显式转换就可以;

在C#中,Sockets类和NetworkStream类发送和接收的类型为字节数组byte[],而它与结构体的转换并不是非常方便,因此也就有了下文

知识储备:

(1) 需要用到Marshal类,该类提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。

(2) 代码的输入和输出都是托管类型(疑问:结构是值类型,也是托管类型??)

(3) 中间需要用非托管类型作为内容的中转;

下面是代码:

从结构体转到字节数组:

    //byte[]是托管的,structObj是托管的,structPtr是非托管的  
//从struct转换到byte[]
public static byte[] StructToBytes(object structObj)
{
//返回类的非托管大小(以字节为单位)
int size = Marshal.SizeOf(structObj);

//分配大小
byte[] bytes = new byte[size];

//从进程的非托管堆中分配内存给structPtr
IntPtr structPtr = Marshal.AllocHGlobal(size);

//将数据从托管对象structObj封送到非托管内存块structPtr
Marshal.StructureToPtr(structObj, structPtr, false);

//Marshal.StructureToPtr(structObj, structPtr, true);
//将数据从非托管内存指针复制到托管 8 位无符号整数数组
Marshal.Copy(structPtr, bytes, 0, size);

//释放以前使用 AllocHGlobal 从进程的非托管内存中分配的内存
Marshal.FreeHGlobal(structPtr);
return bytes;
}

从字节数组转到结构体(strType指明了所要转换的结构体类型):

    //返回类型其实没什么用,从bytes转为strType类型的结构体  
//从byte[]转换为struct
public static object BytesToStruct(byte[] bytes, Type strType)
{
//获取结构体的大小(以字节为单位)
int size = Marshal.SizeOf(strType);
//简单的判断(可以去掉)
if (size > bytes.Length)
{
return null;
}

//从进程的非托管堆中分配内存给structPtr
IntPtr strPtr = Marshal.AllocHGlobal(size);

//将数据从一维托管数组bytes复制到非托管内存指针strPtr
Marshal.Copy(bytes, 0, strPtr, size);

//将数据从非托管内存块封送到新分配的指定类型的托管对象
//将内存空间转换为目标结构体
object obj = Marshal.PtrToStructure(strPtr, strType);

//释放以前使用 AllocHGlobal 从进程的非托管内存中分配的内存
Marshal.FreeHGlobal(strPtr);
return obj;
}

从字节数组类型转为结构体类型(返回值从object直接转为了structType类型的对象)

    //从字节数组转化为结构体  
public StructType ConverBytesToStructure<StructType>(byte[] bytesBuffer)
{
// 检查长度
if (bytesBuffer.Length != Marshal.SizeOf(typeof(StructType)))
{
throw new ArgumentException("bytesBuffer参数和structObject参数字节长度不一致。");
}

//分配一个未托管类型变量
IntPtr bufferHandler = Marshal.AllocHGlobal(bytesBuffer.Length);

//逐个复制,也可以直接用copy()方法
for (int index = 0; index < bytesBuffer.Length; index++)
{
Marshal.WriteByte(bufferHandler, index, bytesBuffer[index]);
}

//从非托管类型转化为托管类型变量
StructType structObject = (StructType)Marshal.PtrToStructure(bufferHandler, typeof(StructType));

//释放非托管类型变量
Marshal.FreeHGlobal(bufferHandler);

return structObject;
}


托管代码 (managed code) :由公共语言运行库环境(而不是直接由操作系统)执行的代码。托管代码应用程序可以获得公共语言运行库服务,例如自动垃圾回收、运行库类型检查和安全支持等。这些服务帮助提供独立于平台和语言的、统一的托管代码应用程序行为。 Unmanaged Code - 非托管代码 :在公共语言运行库环境的外部,由操作系统直接执行的代码。非托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务;它与托管代码不同,后者从公共语言运行库中获得这些服务。非托管代码的英文名是Unmanaged Code ,它是在公共语言运行库环境的外部,由操作系统直接执行的代码。非托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务;它与托管代码不同,后者从公共语言运行库中获得这些服务。