c#中的Struct与Byte[]互转

Standard

原文:http://www.cnblogs.com/juneapple/articles/1769016.html

c#中要让struct转换为byte[],首先要在struct申明中说明struct的结构,如下:


/// <summary>
/// 数据包内容的头信息
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TransHead
{
/// <summary>
/// 本结构的字节大小
/// </summary>
public static readonly int Size = Marshal.SizeOf(typeof(TransHead));

/// <summary>
/// 请求类型
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public RequestType Type;
/// <summary>
/// 请求子类型
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public REFRESH SubType;
/// <summary>
/// 协议版本
/// </summary>
public int Edition;
/// <summary>
/// 标识(系统返回值)
/// </summary>
public int Flag;
}
/// <summary>
/// 数据包内容,头信息加定长数据
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TransHeadAndData
{
/// <summary>
/// 本结构的字节大小
/// </summary>
public static readonly int Size = Marshal.SizeOf(typeof(TransHeadAndData));
/// <summary>
/// 本结构数据体的字节大小(结构大小-结构头)
/// </summary>
public static readonly int SubSize = Size - TransHead.Size;

[MarshalAs(UnmanagedType.Struct)]
public TransHead DataHead;

/// <summary>
/// 具体数据 x460
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 460)]
public byte[] Data;
}
public struct TransUplink
{
/// <summary>
/// 本结构的字节大小
/// </summary>
public static readonly int Size = Marshal.SizeOf(typeof(TransUplink));// - 4;
/// <summary>
/// 数据类型
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public RequestType Type;
/// <summary>
/// /// 数据内容
/// </summary>
[MarshalAs(UnmanagedType.Struct)]
public TransHeadAndData Data;
///// <summary>
///// 发送上去的数据头长度(仅用户客户端,用于接收数据后删除重发列表时的计算,计算结构大小时不加入该项)
///// </summary>
//public int HeadLenght;
}

以下是基本转换方法


/// <summary>
/// 将struct类型转换为byte[]
/// </summary>
public static byte[] StructToBytes(object structObj, int size)
{
IntPtr buffer = Marshal.AllocHGlobal(size);
try//struct_bytes转换
{
Marshal.StructureToPtr(structObj, buffer, false);
byte[] bytes = new byte[size];
Marshal.Copy(buffer, bytes, 0, size);
return bytes;
}
catch (Exception ex)
{
throw new Exception("Error in StructToBytes ! " + ex.Message);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
/// <summary>
/// 将byte[]还原为指定的struct,该函数的泛型仅用于自定义结构
/// startIndex:数组中 Copy 开始位置的从零开始的索引。
/// length:要复制的数组元素的数目。
/// </summary>
public static T BytesToStruct<T>(byte[] bytes, int startIndex, int length)
{
if (bytes == null) return default(T);
if (bytes.Length <= 0) return default(T);
IntPtr buffer = Marshal.AllocHGlobal(length);
try//struct_bytes转换
{
Marshal.Copy(bytes, startIndex, buffer, length);
return (T)Marshal.PtrToStructure(buffer, typeof(T));
}
catch(Exception ex)
{
throw new Exception("Error in BytesToStruct ! " + ex.Message);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}

犹豫频繁的重复调用 IntPtr buffer = Marshal.AllocHGlobal(size);语句,会出现缓存创建错误的情况(溢出之类的错误)

所以在写入列表型数据时使用下面的优化方法


/// <summary>
/// 将List<struct>类型转换为byte[]并写入流
/// </summary>
public static void SetFromListToStream<T>(List<T> inList, int size, FS.FileStreamEx stream, long offset, int listStart, int listCount)
{
IntPtr buffer = Marshal.AllocHGlobal(size);
try//struct_bytes转换
{
if (listCount < 0) listCount = inList.Count;
//若需写入的数据条数太大,则将其分为N块批量写入
int num = listCount;
if (num > 100)
{
num = num / 100;
}
else
{
num = 1;
}

byte[] numByte = new byte[num * size];
byte[] bytes = new byte[size];
for (int i = 0; i < listCount; i++)
{
//批量写入模式
if (num > 1 && num + i < listCount)
{

for (int j = 0; j < num; j++)
{
Marshal.StructureToPtr(inList[listStart + i + j], buffer, false);
Marshal.Copy(buffer, numByte, j * size, size);
}
stream.WriteBytes(offset + i * size, numByte);
i += num - 1;//-1因为外面的for循环中i是+1的
}
//逐条写入模式
else
{
Marshal.StructureToPtr(inList[listStart + i], buffer, false);
Marshal.Copy(buffer, bytes, 0, size);
stream.WriteBytes(offset + i * size, bytes);
}
}
}
catch(Exception ex)
{
throw new Exception("Error in SetFromListToStream ! " + ex.Message);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}

同理,在读出时也应该使用优化方法


public static int SetFromStreamToList<T>(ref List<T> inList, int size, FS.FileStreamEx stream, long offset, int lenght, bool clear)
{
if (clear) inList.Clear();
if (offset < 0) offset = 0;
if (lenght <= 0) lenght = (int)(stream.Length - offset);
byte[] tmpByte = stream.ReadBytes(offset, lenght);
if (tmpByte == null) return ErrCode.Success;
return SetFromBytesToList<T>(ref inList, size, tmpByte, 0, 0);
}

/// <summary>
/// 将byte[]内容转为指定结构,并添加到相应的List中,该函数的泛型仅用于自定义结构
/// </summary>
/// <typeparam name="T">将要转换的类型,同时也是List的类型</typeparam>
/// <param name="inList">将要添加数据的List</param>
/// <param name="size">指定类型的长度,传入长度是为了避免每次都去计算结构长度</param>
/// <param name="inByte">将要转换的数据</param>
/// <param name="offset">便宜量,从数组中指定位置开始转换,取值范围:0至lenght</param>
/// <param name="lenght">将要转换的数据长度,
/// 若lenght大于等于inByte.Lenght则lenght=inByte.Lenght;
/// 若lenght小于等于0则lenght=inByte.Lenght</param>
/// <param name="clear">添加前是否清除旧数据</param>
/// <returns>是否成功</returns>
public static int SetFromBytesToList<T>(ref List<T> inList, int size, byte[] inByte, long offset, int lenght, bool clear)
{
lock (inList)
{
if (clear) inList.Clear();
if (lenght <= 0) lenght = inByte.Length;
if (lenght <= 0) return ErrCode.Success;

IntPtr buffer = Marshal.AllocHGlobal(size);
int i;
try//struct_bytes转换
{
for (i = (int)offset; i + size <= lenght; i += size)
{
Marshal.Copy(inByte, i, buffer, size);
inList.Add((T)Marshal.PtrToStructure(buffer, typeof(T)));
}
}
catch(Exception ex)
{
throw new Exception("Error in SetFromBytesToList ! " + ex.Message);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
return ErrCode.Success;
}
}