加入收藏 | 设为首页 | 会员中心 | 我要投稿 我爱故事小小网_铜陵站长网 (http://www.0562zz.com/)- 视频终端、云渲染、应用安全、数据安全、安全管理!
当前位置: 首页 > 教程 > 正文

C#联合Union的达成方式

发布时间:2021-11-18 21:00:43 所属栏目:教程 来源:互联网
导读:一.基础篇 C#不像C++,他本身是没有联合Union的,但是可以通过手动控制结构体每个元素的位置来实现,这需要结合使用StructLayoutAttribute、LayoutKind以及FieldOffsetAttribute。使用它们的时候必须引用System.Runtime.InteropServices下面是我写的模拟U的联

一.基础篇
 
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);

(编辑:我爱故事小小网_铜陵站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读