歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

C#聯合Union的實現方式

一.基礎篇

C#不像C++,他本身是沒有聯合Union的,但是可以通過手動控制結構體每個元素的位置來實現,這需要結合使用StructLayoutAttribute、LayoutKind以及FieldOffsetAttribute。使用它們的時候必須引用System.Runtime.InteropServices下面是我寫的模擬U的聯合。

[StructLayout(LayoutKind.Explicit, Size = 4)]
struct U
{
    [FieldOffset(0)]
    public byte b0;
    [FieldOffset(1)]
    public byte b1;
    [FieldOffset(2)]
    public byte b2;
    [FieldOffset(3)]
    public byte b3;
 
    [FieldOffset(0)]
    public int i;
 
    [FieldOffset(0)]
    public float f;
}

我們知道聯合中每個數據成員都在相同的內存地址開始,所以我們要通過[FieldOffset(0)]應用到U的每一個成員,意思就是讓這些成員處於同一個開始位置。當然,我們得事先告訴.NET這些成員的內存布局由我們來作主,所以要使用LayoutKind.Explicit枚舉然後傳遞給StructLayoutAttribute,並應用到U上,這樣.Net就不會再干涉該struct的成員在內存中的布局了。並且我定義了U的Size為12,當然你也可以不定義U的Size。

而且使用聯合進行數據轉換比BitConverter要快。測試用例如下:

{
    DateTime past = DateTime.Now;
    int length = 500000 * 3 * 3;
    for (int i = 0; i < length; i++)
    {
        U a = new U();
        a.b0 = 0xFF;
        a.b1 = 0xFF;
        int res = a.i;
    }
    DateTime now = DateTime.Now;
    Console.WriteLine((now - past));
}
 
{
    DateTime past = DateTime.Now;
    int length = 500000 * 3 * 3;
    for (int i = 0; i < length; i++)
    {
        byte[] a = { 0xFF, 0x0F, 0x0F, 0 };
        object b = a;
        int res = BitConverter.ToInt32(a, 0);
    }
    DateTime now = DateTime.Now;
    Console.WriteLine((now - past));
}

二.進階篇

之前的方法還存在好多問題,比如數組沒法放入聯合中,會提示值和引用沖突什麼的。

今天又研究了一下,利用C#中可以使用指針的特性,結合unsafe和fixed,實現數組類型和普通值類型的共存。

方法①  數組類型和普通值類型的共存——固定大小的緩沖區

利用固定大小的緩沖區(fixed)實現數組類型和普通值類型的共存

[StructLayoutAttribute(LayoutKind.Explicit, Pack = 1)]
public unsafe struct A
{
    [FieldOffset(0)]
    public int a;
    [FieldOffset(0)]
    public byte b;
    [FieldOffset(0)]
    public float c;
    [FieldOffset(0)]
    public fixed byte arr[9];
};

方法②  結構體轉字節數組——1).使用聯合 2).使用指針強制轉換

1).使用聯合,利用一個和原結構體等長的fixed byte buff[n],這個buff就是我們要的直接數組,訪問時需要通過fixed (byte* ta = a.buff) {}來訪問。

[StructLayoutAttribute(LayoutKind.Explicit, Pack = 1)]
public unsafe struct A
{
    [FieldOffset(0)]
    public int a;
    [FieldOffset(4)]
    public byte b;
    [FieldOffset(5)]
    public float c;
    [FieldOffset(0)]
    public fixed byte buff[9];
};

2).直接使用指針強制轉換,通過fixed,先將結構體轉換為void *,再將其轉化為byte* b。

fixed (void * ta = &a)
{
    byte* b = (byte*)ta ;
}

3).最後通過IntPtr拷貝到C#標准的byte[]中。

byte[] Dbuff = new byte[9];
IntPtr pstart = new IntPtr(a);
Marshal.Copy(pstart, Dbuff, 0, 9);

Copyright © Linux教程網 All Rights Reserved