C#中实现byte[]与任意对象互换(服务端通讯专用)

C++中,我们可以非常方便的将网络通讯接收来的char*缓冲区转成任意类型的结构体,并从中提取必要信息,只需要一个结构体类型指针的强制转换即可。

但是在C#中,所有涉及到内存及指针的操作均被判定为不安全操作,使得上述机制的实现变得复杂化。

要在C#中便捷的实现网络通讯缓冲区byte[]与任意类型对象的相互转换,常用的方法大致有三:

1.序列化与反序列化

[c-sharp]  view plain copy print ?
  1. public static byte[] ObjectToBytes(object obj)  
  2. {  
  3.     using (MemoryStream ms = new MemoryStream())  
  4.     {  
  5.         IFormatter formatter = new BinaryFormatter();  
  6.         formatter.Serialize(ms, obj);  
  7.         return ms.GetBuffer();  
  8.     }  
  9. }  
  10.   
  11. public static object BytesToObject(byte[] Bytes)  
  12. {  
  13.     using (MemoryStream ms = new MemoryStream(Bytes))  
  14.     {  
  15.         IFormatter formatter = new BinaryFormatter();  
  16.         return formatter.Deserialize(ms);  
  17.     }  
  18. }  

注意:传入的结构体类型一定是“可序列化的(Serializable)”

优点:安全可靠

使用这种方法一般不会产生其他的副作用,其安全度在C#的可控范围之内。

缺点:浪费资源、效率偏低

使用这种方法会造成不必要的资源浪费:

struct原有大小 序列化之后的byte[]大小
100                   256
800                   1024
1000                 2048
3000                 4096
6000                 8192
10000               16384
产生出不必要的冗余数据,效率的降低将是必然结果。
2.内存流配合BitConvert
BitConvert类提供了丰富的byte[]与其他基础类型(Int16、Int32等)间的互换方法。
byte[]转成特定struct时,需要实现new一个新的struct。之后借助BitConvert将接收来的byte[]缓冲区特定段按照struct各部分定义转化为相应类型,并赋值给struct成员。反之亦然。
优点:安全可靠,性能可观

使用这种方法一般不会产生其他的副作用,其安全度在C#的可控范围之内。

缺点:资源浪费,不易实现

使用这种方法造成了额外的内存申请与复制。

另外,针对每种特定类型的数据包,需要提供特定的包解析与生成机制。

可以考虑通过包类型间的继承关系降低后期维护的难度。比如:定义一种基类型的数据包,子类数据包继承基类数据包的成员,并重写基类的解析与生成方法等等。

3.Marshal

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

[c-sharp]  view plain copy print ?
  1. public static byte[] StructToBytes(Object obj)  
  2. {  
  3.     int size = Marshal.SizeOf(obj);  
  4.     byte[] bytes = new byte[size];  
  5.     IntPtr arrPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);  
  6.     Marshal.StructureToPtr(obj, arrPtr, true);  
  7.     return bytes;  
  8. }  
  9.   
  10. public static Object BytesToStruct(byte[] bytes,Type StructStyle)  
  11. {  
  12.     IntPtr arrPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);  
  13.     return Marshal.PtrToStructure(arrPtr, StructStyle);  
  14. }  

.net 4.0 实现方法:

[csharp]  view plain copy print ?
  1. /// <summary>  
  2. /// 由结构体转换为byte数组  
  3. /// </summary>  
  4. public static byte[] StructureToByte<T>(T structure)  
  5. {  
  6.     int size = Marshal.SizeOf(typeof(T));  
  7.     byte[] buffer = new byte[size];  
  8.     IntPtr bufferIntPtr = Marshal.AllocHGlobal(size);  
  9.     try  
  10.     {  
  11.         Marshal.StructureToPtr(structure, bufferIntPtr, true);  
  12.         Marshal.Copy(bufferIntPtr, buffer, 0, size);  
  13.     }  
  14.     finally  
  15.     {  
  16.         Marshal.FreeHGlobal(bufferIntPtr);  
  17.     }  
  18.     return buffer;  
  19. }  
  20.   
  21. /// <summary>  
  22. /// 由byte数组转换为结构体  
  23. /// </summary>  
  24. public static T ByteToStructure<T>(byte[] dataBuffer)  
  25. {  
  26.     object structure = null;  
  27.     int size = Marshal.SizeOf(typeof(T));  
  28.     IntPtr allocIntPtr = Marshal.AllocHGlobal(size);  
  29.     try  
  30.     {  
  31.         Marshal.Copy(dataBuffer, 0, allocIntPtr, size);  
  32.         structure = Marshal.PtrToStructure(allocIntPtr, typeof(T));  
  33.     }  
  34.     finally  
  35.     {  
  36.         Marshal.FreeHGlobal(allocIntPtr);  
  37.     }  
  38.     return (T)structure;  
  39. }  

优点:高效、易实现

这个方法的实际效果与C++类似,不存在额外的内存申请及拷贝动作。

缺点:不安全、功能受限

1> 某些特殊情况下,该方法会失效。

2> 由于涉及到了与非托管内存间的转换,安全度降低。

3> C#中的Struct功能弱化,无法有效组织数据包继承关系。

4> 在一些特有环境下,Marshal的权限并未全部公开,比如Silverlight。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值