用過C/C++的人都知道有個union,特別好用,似乎char數組到short,int,float等的轉換無所不能,也確實是能,並且用起來十分方便。那C#為什麼沒有這個關鍵字呢?怎麼實現這個功能?其實C#只是沒有了這個關鍵字,但是功能是能實現的,而且也是非常方便,並且是安全的。網上有人用Struct ...
用過C/C++的人都知道有個union,特別好用,似乎char數組到short,int,float等的轉換無所不能,也確實是能,並且用起來十分方便。
那C#為什麼沒有這個關鍵字呢?怎麼實現這個功能?其實C#只是沒有了這個關鍵字,但是功能是能實現的,而且也是非常方便,並且是安全的。
網上有人用StructLayout特性來實現union,也確實是實現了一些功能。
比如:
C/C++:
union {
unsigned char ch
short ;
int i;
};
C#:
[StructLayout(LayoutKind.Explicit)]
public struct Class1
{
[FieldOffset(0)]
public byte b;
[FieldOffset(0)]
public short s;
[FieldOffset(0)]
public int i;
}
就可以實現。
但是我要是寫個:
union {
unsigned char ch[4];
int i;
float f;
} temp;
硬是用C#沒有模擬出來,估計我還沒有找著合適的方法。因為我寫
[StructLayout(LayoutKind.Explicit)]
public struct Class1
{
[FieldOffset(0)]
public byte[4] b;
[FieldOffset(0)]
public short s;
[FieldOffset(0)]
public int i;
}
這玩意是編譯不通過的。然後折騰了半天,沒有折騰出來。後來又回到C/C++想了一番,似乎有些認識。
C/C++用union其實就是使用同一塊記憶體存儲不同類型的數據,說白了,就是一塊公用的記憶體,你用啥讀取出來就是啥內容。其實電腦中的記憶體本身也就是這樣,你定義一個int i;然後電腦會在記憶體棧上開闢一塊空間,並且這塊記憶體指明瞭是int類型,但是我們經常看到(int)data,(int*)pt等操作,說明可以強制轉換。強制轉換不是說把這幾塊記憶體的值改變了,只是臨時改變了讀取方式,然後用這種方式讀取這塊記憶體。那這樣說來是不是也可以不用union來實現char數組與其他類型之間的轉換,答案是必須可以。
比如:
unsigned char chArr[4] = "";
float f1 = 45.56f;
memcpy(chArr, &f1, sizeof(float));
// 運行結果:113 61 54 66
printf("%d\t%d\t%d\t%d\n", chArr[0], chArr[1], chArr[2], chArr[3]);
float f2 = 0.00f;
memcpy(&f2, chArr, sizeof(float));
printf("%0.2f\n", f2);
float f3 = *(float *)chArr;
printf("%0.2f\n", f3);
char *pch = (char *)&f3;
// 運行結果:113 61 54 66
printf("%d\t%d\t%d\t%d\n", pch[0], pch[1], pch[2], pch[3]);
那好問題來了,C#怎麼實現?
那好,答案也來了。當然是用BitConvert。
比如:
float f = 45.56f;
byte[] b = BitConverter.GetBytes(f);
Console.WriteLine("bArr\t: {0}\t{1}\t{2}\t{3}", b[0], b[1], b[2], b[3]);
float f2 = BitConverter.ToSingle(b, 0);
Console.WriteLine("f2\t: {0}", f2);
完全木有問題啊,而且還安全。
最後呢,咱們看看微軟是怎麼給咱實現的。
// Converts a float into an array of bytes with length
// four.
[System.Security.SecuritySafeCritical] // auto-generated
public unsafe static byte[] GetBytes(float value)
{
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Ensures(Contract.Result<byte[]>().Length == 4);
return GetBytes(*(int*)&value);
}
...
// Converts an int into an array of bytes with length
// four.
[System.Security.SecuritySafeCritical] // auto-generated
public unsafe static byte[] GetBytes(int value)
{
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Ensures(Contract.Result<byte[]>().Length == 4);
byte[] bytes = new byte[4];
fixed(byte* b = bytes)
*((int*)b) = value;
return bytes;
}
看見了嗎?是不是跟上面的C/C++代碼很像。其實就是C/C++代碼。如果你看不到這段代碼,也許你還真不知道,原來以前自己的C/C++代碼被搬到了這裡。但是微軟的公司的代碼可不是我寫的C/C++那麼簡單的轉換,微軟程式員是做了安全檢查的。你如果將3個byte的數組轉換到float,那對不起,玩不了,你得補一個位元組。
好了,給大家附上微軟C#開源的源代碼地址:
https://referencesource.microsoft.com/#mscorlib/system/bitconverter.cs,9108fa2d0b37805b