C#中實現byte[]與任意物件互換(服務端通訊專用)
C++中,我們可以非常方便的將網路通訊接收來的char*緩衝區轉成任意型別的結構體,並從中提取必要資訊,只需要一個結構體型別指標的強制轉換即可。
但是在C#中,所有涉及到記憶體及指標的操作均被判定為不安全操作,使得上述機制的實現變得複雜化。
要在C#中便捷的實現網路通訊緩衝區byte[]與任意型別物件的相互轉換,常用的方法大致有三:
1.序列化與反序列化
- publicstaticbyte[]ObjectToBytes(objectobj)
- {
- using(MemoryStreamms=newMemoryStream())
- {
- IFormatterformatter=newBinaryFormatter();
- formatter.Serialize(ms,obj);
- returnms.GetBuffer();
- }
- }
- publicstaticobjectBytesToObject(byte[]Bytes)
- {
- using(MemoryStreamms=newMemoryStream(Bytes))
- {
- IFormatterformatter=newBinaryFormatter();
- returnformatter.Deserialize(ms);
- }
- }
注意:傳入的結構體型別一定是“可序列化的(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
該類對外提供了一個方法集,這些方法用於分配非託管記憶體、複製非託管記憶體塊、將託管型別轉換為非託管型別,此外還提供了在與非託管程式碼互動時使用的其他雜項方法。
- publicstaticbyte[]StructToBytes(Objectobj)
- {
- intsize=Marshal.SizeOf(obj);
- byte[]bytes=newbyte[size];
- IntPtrarrPtr=Marshal.UnsafeAddrOfPinnedArrayElement(bytes,0);
- Marshal.StructureToPtr(obj,arrPtr,true);
- returnbytes;
- }
- publicstaticObjectBytesToStruct(byte[]bytes,TypeStructStyle)
- {
- IntPtrarrPtr=Marshal.UnsafeAddrOfPinnedArrayElement(bytes,0);
- returnMarshal.PtrToStructure(arrPtr,StructStyle);
- }
net 4.0 實現方法:
- ///<summary>
- ///由結構體轉換為byte陣列
- ///</summary>
- publicstaticbyte[]StructureToByte<T>(Tstructure)
- {
- intsize=Marshal.SizeOf(typeof(T));
- byte[]buffer=newbyte[size];
- IntPtrbufferIntPtr=Marshal.AllocHGlobal(size);
- try
- {
- Marshal.StructureToPtr(structure,bufferIntPtr,true);
- Marshal.Copy(bufferIntPtr,buffer,0,size);
- }
- finally
- {
- Marshal.FreeHGlobal(bufferIntPtr);
- }
- returnbuffer;
- }
- ///<summary>
- ///由byte陣列轉換為結構體
- ///</summary>
- publicstaticTByteToStructure<T>(byte[]dataBuffer)
- {
- objectstructure=null;
- intsize=Marshal.SizeOf(typeof(T));
- IntPtrallocIntPtr=Marshal.AllocHGlobal(size);
- try
- {
- Marshal.Copy(dataBuffer,0,allocIntPtr,size);
- structure=Marshal.PtrToStructure(allocIntPtr,typeof(T));
- }
- finally
- {
- Marshal.FreeHGlobal(allocIntPtr);
- }
- return(T)structure;
- }
優點:高效、易實現
這個方法的實際效果與C++類似,不存在額外的記憶體申請及拷貝動作。
缺點:不安全、功能受限
1> 某些特殊情況下,該方法會失效。
2> 由於涉及到了與非託管記憶體間的轉換,安全度降低。
3> C#中的Struct功能弱化,無法有效組織資料包繼承關係。
4> 在一些特有環境下,Marshal的許可權並未全部公開,比如Silverlight。
轉https://blog.csdn.net/qq_31967569/article/details/81166780