1. 程式人生 > 其它 >C#中實現byte[]與任意物件互換(服務端通訊專用)

C#中實現byte[]與任意物件互換(服務端通訊專用)

https://blog.csdn.net/qq_31967569/article/details/81166780

C++中,我們可以非常方便的將網路通訊接收來的char*緩衝區轉成任意型別的結構體,並從中提取必要資訊,只需要一個結構體型別指標的強制轉換即可。

但是在C#中,所有涉及到記憶體及指標的操作均被判定為不安全操作,使得上述機制的實現變得複雜化。

要在C#中便捷的實現網路通訊緩衝區byte[]與任意型別物件的相互轉換,常用的方法大致有三:

1.序列化與反序列化

  1. publicstaticbyte[]ObjectToBytes(objectobj)
  2. {
  3. using(MemoryStreamms=newMemoryStream())
  4. {
  5. IFormatterformatter=newBinaryFormatter();
  6. formatter.Serialize(ms,obj);
  7. returnms.GetBuffer();
  8. }
  9. }
  10. publicstaticobjectBytesToObject(byte[]Bytes)
  11. {
  12. using(MemoryStreamms=newMemoryStream(Bytes))
  13. {
  14. IFormatterformatter=newBinaryFormatter();
  15. returnformatter.Deserialize(ms);
  16. }
  17. }

注意:傳入的結構體型別一定是“可序列化的(Serializable)”

優點:安全可靠

使用這種方法一般不會產生其他的副作用,其安全度在C#的可控範圍之內。

缺點:浪費資源、效率偏低

使用這種方法會造成不必要的資源浪費:

struct原有大小 序列化之後的byte[]大小

100 256
800 1024
10002048
3000 4096
60008192
10000 16384

產生出不必要的冗餘資料,效率的降低將是必然結果。

2.記憶體流配合BitConvert

BitConvert類提供了豐富的byte[]與其他基礎型別(Int16、Int32等)間的互換方法。

byte[]轉成特定struct時,需要實現new一個新的struct。之後藉助BitConvert將接收來的byte[]緩衝區特定段按照struct各部分定義轉化為相應型別,並賦值給struct成員。反之亦然。

優點:安全可靠,效能可觀

使用這種方法一般不會產生其他的副作用,其安全度在C#的可控範圍之內。

缺點:資源浪費,不易實現

使用這種方法造成了額外的記憶體申請與複製。

另外,針對每種特定型別的資料包,需要提供特定的包解析與生成機制。

可以考慮通過包型別間的繼承關係降低後期維護的難度。比如:定義一種基型別的資料包,子類資料包繼承基類資料包的成員,並重寫基類的解析與生成方法等等。

3.Marshal

該類對外提供了一個方法集,這些方法用於分配非託管記憶體、複製非託管記憶體塊、將託管型別轉換為非託管型別,此外還提供了在與非託管程式碼互動時使用的其他雜項方法。

  1. publicstaticbyte[]StructToBytes(Objectobj)
  2. {
  3. intsize=Marshal.SizeOf(obj);
  4. byte[]bytes=newbyte[size];
  5. IntPtrarrPtr=Marshal.UnsafeAddrOfPinnedArrayElement(bytes,0);
  6. Marshal.StructureToPtr(obj,arrPtr,true);
  7. returnbytes;
  8. }
  9. publicstaticObjectBytesToStruct(byte[]bytes,TypeStructStyle)
  10. {
  11. IntPtrarrPtr=Marshal.UnsafeAddrOfPinnedArrayElement(bytes,0);
  12. returnMarshal.PtrToStructure(arrPtr,StructStyle);
  13. }

net 4.0 實現方法:

  1. ///<summary>
  2. ///由結構體轉換為byte陣列
  3. ///</summary>
  4. publicstaticbyte[]StructureToByte<T>(Tstructure)
  5. {
  6. intsize=Marshal.SizeOf(typeof(T));
  7. byte[]buffer=newbyte[size];
  8. IntPtrbufferIntPtr=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. returnbuffer;
  19. }
  20. ///<summary>
  21. ///由byte陣列轉換為結構體
  22. ///</summary>
  23. publicstaticTByteToStructure<T>(byte[]dataBuffer)
  24. {
  25. objectstructure=null;
  26. intsize=Marshal.SizeOf(typeof(T));
  27. IntPtrallocIntPtr=Marshal.AllocHGlobal(size);
  28. try
  29. {
  30. Marshal.Copy(dataBuffer,0,allocIntPtr,size);
  31. structure=Marshal.PtrToStructure(allocIntPtr,typeof(T));
  32. }
  33. finally
  34. {
  35. Marshal.FreeHGlobal(allocIntPtr);
  36. }
  37. return(T)structure;
  38. }

優點:高效、易實現

這個方法的實際效果與C++類似,不存在額外的記憶體申請及拷貝動作。

缺點:不安全、功能受限

1> 某些特殊情況下,該方法會失效。

2> 由於涉及到了與非託管記憶體間的轉換,安全度降低。

3> C#中的Struct功能弱化,無法有效組織資料包繼承關係。

4> 在一些特有環境下,Marshal的許可權並未全部公開,比如Silverlight。

轉https://blog.csdn.net/qq_31967569/article/details/81166780