在通信过程中,一般我们都会操作到字节数组.特别是希望在不同语言编程进行操作的时候.
虽然C#提供了序列化的支持,不用字节数组也行.但操作字节数组肯定会碰到.
一般都会采用结构来表示字节数组.但结构与字节数组直接的转换实在很麻烦.
字节操作不但容易出错,而且每增加一个结构,就自己实现一遍,实在是烦不胜烦.
有没有简单的方法呢?当然有.可以采用非托管区的一些方法来实现.
首先,导入命名空间:System.Runtime.InteropServices;
定义结构的时候,要给结构指定特性.
如:
//注意这个特性不能少
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct TestStruct
{
public int c;
//字符串,SizeConst为字符串的最大长度
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string str;
//int数组,SizeConst表示数组的个数,在转换成
//byte数组前必须先初始化数组,再使用,初始化
//的数组长度必须和SizeConst一致,例test = new int[6];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public int[] test;
}
你可以修改特性的属性,来更改在内存的布局和排列.
好,接下来将结果转换为字节数据.为了支持各种结构,我采用泛型的做法.这样编译器可以帮助我们检查类型是否正确.
///
/// 结构体转byte数组
///
/// 要转换的结构体
/// 转换后的byte数组
public static byte[] StructToBytes(T structObj)where T:struct
{
//得到结构体的大小
int size = Marshal.SizeOf(structObj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将结构体拷到分配好的内存空间
Marshal.StructureToPtr(structObj, structPtr, false);
//从内存空间拷到byte数组
Marshal.Copy(structPtr, bytes, 0, size);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回byte数组
return bytes;
}
将字节数组转换为指定结构
///
/// byte数组转结构体
///
/// byte数组
/// 转换后的结构体
public static T BytesToStuct(byte[] bytes)where T:struct
{
T type = new T();
//得到结构体的大小
int size = Marshal.SizeOf(type);
//byte数组长度小于结构体的大小
if (size > bytes.Length)
{
//返回空
return (default (T));
}
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将byte数组拷到分配好的内存空间
Marshal.Copy(bytes,0,structPtr,size);
//将内存空间转换为目标结构体
object obj = Marshal.PtrToStructure(structPtr, type.GetType ());
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回结构体
return (T)obj;
}
虽然我对T类型进行了限制,但如果结构没有指定特性的话,运行不会出现问题,但逻辑可能会出现一些问题,出现了你不想要的结果.这是必须注意的.
那怎么使用这些代码呢?
public static void TestC()
{
TestStruct t = new TestStruct();
t.c = 100;
t.str = "12345";
t.test = new int[] { 1, 2, 5, 6, 9,4 };
byte[] data = StructToBytes(t);
foreach (var item in data)
{
Console.WriteLine(item);
}
TestStruct tm = BytesToStuct(data);
Console.WriteLine("C:{0} str={1}",tm.c,tm.str );
foreach (var item in tm.test)
{
Console.WriteLine(item);
}
}