Home  >  Article  >  php教程  >  C#联合Union的实现方式

C#联合Union的实现方式

WBOY
WBOYOriginal
2016-07-06 13:30:201063browse

一.基础篇 C#不像C++,他本身是没有联合Union的,但是可以通过手动控制结构体每个元素的位置来实现,这需要结合使用 StructLayoutAttribute、LayoutKind以及FieldOffsetAttribute 。使用它们的时候必须引用System.Runtime.InteropServices下面是我写的模拟U的

一.基础篇

        C#不像C++,他本身是没有联合Union的,但是可以通过手动控制结构体每个元素的位置来实现,这需要结合使用StructLayoutAttribute、LayoutKind以及FieldOffsetAttribute。使用它们的时候必须引用System.Runtime.InteropServices下面是我写的模拟U的联合。

[StructLayout(LayoutKind.Explicit, Size = <span style="color: #800080;">4</span><span style="color: #000000;">)]
</span><span style="color: #0000ff;">struct</span><span style="color: #000000;"> U
{
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">byte</span><span style="color: #000000;"> b0;
    [FieldOffset(</span><span style="color: #800080;">1</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">byte</span><span style="color: #000000;"> b1;
    [FieldOffset(</span><span style="color: #800080;">2</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">byte</span><span style="color: #000000;"> b2;
    [FieldOffset(</span><span style="color: #800080;">3</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">byte</span><span style="color: #000000;"> b3;
 
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> i;
 
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> f;
}</span>

        我们知道联合中每个数据成员都在相同的内存地址开始,所以我们要通过[FieldOffset(0)]应用到U的每一个成员,意思就是让这些成员处于同一个开始位置。当然,我们得事先告诉.NET这些成员的内存布局由我们来作主,所以要使用LayoutKind.Explicit枚举然后传递给StructLayoutAttribute,并应用到U上,这样.Net就不会再干涉该struct的成员在内存中的布局了。并且我定义了U的Size为12,当然你也可以不定义U的Size。

而且使用联合进行数据转换比BitConverter要快。测试用例如下:

<span style="color: #000000;">{
    DateTime past </span>=<span style="color: #000000;"> DateTime.Now;
    </span><span style="color: #0000ff;">int</span> length = <span style="color: #800080;">500000</span> * <span style="color: #800080;">3</span> * <span style="color: #800080;">3</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i < length; i++<span style="color: #000000;">)
    {
        U a </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> U();
        a.b0 </span>= <span style="color: #800080;">0xFF</span><span style="color: #000000;">;
        a.b1 </span>= <span style="color: #800080;">0xFF</span><span style="color: #000000;">;
        </span><span style="color: #0000ff;">int</span> res =<span style="color: #000000;"> a.i;
    }
    DateTime now </span>=<span style="color: #000000;"> DateTime.Now;
    Console.WriteLine((now </span>-<span style="color: #000000;"> past));
}
 
{
    DateTime past </span>=<span style="color: #000000;"> DateTime.Now;
    </span><span style="color: #0000ff;">int</span> length = <span style="color: #800080;">500000</span> * <span style="color: #800080;">3</span> * <span style="color: #800080;">3</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i < length; i++<span style="color: #000000;">)
    {
        </span><span style="color: #0000ff;">byte</span>[] a = { <span style="color: #800080;">0xFF</span>, <span style="color: #800080;">0x0F</span>, <span style="color: #800080;">0x0F</span>, <span style="color: #800080;">0</span><span style="color: #000000;"> };
        </span><span style="color: #0000ff;">object</span> b =<span style="color: #000000;"> a;
        </span><span style="color: #0000ff;">int</span> res = BitConverter.ToInt32(a, <span style="color: #800080;">0</span><span style="color: #000000;">);
    }
    DateTime now </span>=<span style="color: #000000;"> DateTime.Now;
    Console.WriteLine((now </span>-<span style="color: #000000;"> past));
}</span>

二.进阶篇

之前的方法还存在好多问题,比如数组没法放入联合中,会提示值和引用冲突什么的。

今天又研究了一下,利用C#中可以使用指针的特性,结合unsafe和fixed,实现数组类型和普通值类型的共存

方法①  数组类型和普通值类型的共存——固定大小的缓冲区

利用固定大小的缓冲区(fixed)实现数组类型和普通值类型的共存

[StructLayoutAttribute(LayoutKind.Explicit, Pack = <span style="color: #800080;">1</span><span style="color: #000000;">)]
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">unsafe</span> <span style="color: #0000ff;">struct</span><span style="color: #000000;"> A
{
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> a;
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">byte</span><span style="color: #000000;"> b;
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> c;
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">fixed</span> <span style="color: #0000ff;">byte</span> arr[<span style="color: #800080;">9</span><span style="color: #000000;">];
};</span>

方法②  结构体转字节数组——1).使用联合 2).使用指针强制转换

1).使用联合,利用一个和原结构体等长的fixed byte buff[n],这个buff就是我们要的直接数组,访问时需要通过fixed (byte* ta = a.buff) {}来访问。

[StructLayoutAttribute(LayoutKind.Explicit, Pack = <span style="color: #800080;">1</span><span style="color: #000000;">)]
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">unsafe</span> <span style="color: #0000ff;">struct</span><span style="color: #000000;"> A
{
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> a;
    [FieldOffset(</span><span style="color: #800080;">4</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">byte</span><span style="color: #000000;"> b;
    [FieldOffset(</span><span style="color: #800080;">5</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> c;
    [FieldOffset(</span><span style="color: #800080;">0</span><span style="color: #000000;">)]
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">fixed</span> <span style="color: #0000ff;">byte</span> buff[<span style="color: #800080;">9</span><span style="color: #000000;">];
};</span>

2).直接使用指针强制转换,通过fixed,先将结构体转换为void *,再将其转化为byte* b。

<span style="color: #0000ff;">fixed</span> (<span style="color: #0000ff;">void</span> * ta = &<span style="color: #000000;">a)
{
    </span><span style="color: #0000ff;">byte</span>* b = (<span style="color: #0000ff;">byte</span>*<span style="color: #000000;">)ta ;
}</span>

3).最后通过IntPtr拷贝到C#标准的byte[]中。

<span style="color: #0000ff;">byte</span>[] Dbuff = <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">byte</span>[<span style="color: #800080;">9</span><span style="color: #000000;">];
IntPtr pstart </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> IntPtr(a);
Marshal.Copy(pstart, Dbuff, </span><span style="color: #800080;">0</span>, <span style="color: #800080;">9</span>);

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn