C#不用union,而是有更好的方式實現



用過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



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM