在通信過程中,一般我們都會操作到字節數組.特別是希望在不同語言編程進行操作的時候.
雖然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);
}
}