在C#中使用Union联合体
Union联合体(共用体)
这里稍微简单介绍一下union这种类型。
有时候需要使几种不同类型的变量存放到同一段内存单元中,例如:可把一个整型变量、一个字符串变量放在同一个地址开始的内存单元中。以上两个变量在内存中占用的字节数不同,但都从同一个地址开始存放,也就是使用覆盖技术,几个变量互相覆盖。这种使用几个不同的变量共同占用同一段内存的结构,称为union。
1 union
2 {
3 int a;
4 char b;
5 };
union的特点是:
1、同一个内存段可以用来存放几种不同类型的成员,但在同一时刻只能存放其中一种。
2、union起作用的成员是最后一次存放的变量,在存入一个新的成员后原有的成员就失去作用。
union和struct的区别
struct:所占内存长度是各成员占的内存之和,每个成员分别占用某个自己的内存单元
union:所占内存长度等于最长的成员的长度
在C#中使用union
在C#中与union交互,需要用到StructLayoutAttribute特性和FieldOffsetAttribute特性,关于这两个特性的介绍,可以参考P/Invoke各种总结(一、使用StructLayout特性来控制内存结构) - zhaotianff - 博客园
其实在知道union的原理后,在C#中进行映射也是非常简单。
如前面示例代码中的union,在C#中的表示形式如下:
1 [StructLayout(LayoutKind.Explicit)]
2 struct MyStruct
3 {
4 [FieldOffset(0)]int a;
5 [FieldOffset(0)]char b;
6 };
在上一篇文章中,介绍了 StructLayoutAttribute类的Pack字段,这里再介绍一下另外一个字段,StructLayoutAttribute.Size字段,Size字段用于指示类或结构的绝对大小。
在使用union时,如果不是C#中的值类型,就需要指定大小。
测试程序:
最简单的情况:
C++
1 union MYUNION
2 {
3 int b;
4 double d;
5 };
6
7 extern "C" __declspec(dllexport) MYUNION GetMyUnion();
8
9 extern "C" __declspec(dllexport) MYUNION GetMyUnion()
10 {
11 MYUNION myunion;
12 myunion.b = 10;
13 return myunion;
14 }
C#
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Runtime.InteropServices;
7
8 namespace UnionTest
9 {
10 class Program
11 {
12 [DllImport("UnionLib.dll")]
13 public static extern MyUnion GetMyUnion();
14
15 static void Main(string[] args)
16 {
17
18 //只有值类型的情况
19 var myunion = GetMyUnion();
20 Console.WriteLine(myunion.b);
21 }
22 }
23 }
运行结果:
![]()
带引用类型的情况:
需要注意的是:值类型和引用类型不允许重叠。所以在单独使用带引用类型的union时,需要分开处理。
这里定义了两个导出函数,TestUnion2函数可以传入MYUNION2,并进行输出。GetMyUnion2函数直接返回MYUNION2
C++
1 union MYUNION2
2 {
3 int i;
4 char str[128];
5 };
6
7 extern "C" __declspec(dllexport) void TestUnion2(MYUNION2 u, int type);
8
9 extern "C" __declspec(dllexport) void TestUnion2(MYUNION2 u, int type)
10 {
11 if (type == 1)
12 {
13 std::cout << u.i << std::endl;
14 }
15 else
16 {
17 std::cout << u.str << std::endl;
18 }
19 }
20
21 extern "C" __declspec(dllexport) MYUNION2 GetMyUnion2();
22
23 extern "C" __declspec(dllexport) MYUNION2 GetMyUnion2()
24 {
25 MYUNION2 myunion2;
26 strcpy_s(myunion2.str, 11, "HelloWorld");
27 return myunion2;
28 }
C#
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Runtime.InteropServices;
7
8 namespace UnionTest
9 {
10 class Program
11 {
12 [DllImport("UnionLib.dll")]
13 public static extern IntPtr GetMyUnion2();
14
15
16 [DllImport("UnionLib.dll")]
17 public static extern void TestUnion2(MyUnion2_INT mm, int i);
18
19 [DllImport("UnionLib.dll")]
20 public static extern void TestUnion2(MyUnion2_STR mm,int i);
21
22 static void Main(string[] args)
23 {
24 //使用了引用类型无法直接通过签名返回,需要进行转换
25 //如果这里是使用了MYUNION2中的i字段,可以直接使用MyUnion2_INT作为返回值
26 var myunion2Ptr = GetMyUnion2();
27 MyUnion2_STR unionStr = new MyUnion2_STR();
28 unionStr = (MyUnion2_STR)Marshal.PtrToStructure(myunion2Ptr, typeof(MyUnion2_STR));
29 Console.WriteLine(unionStr.str);
30
31 //注意:值类型和引用类型不允许重叠
32 //在使用引用类型的情况下,无法直接返回union
33 MyUnion2_INT mu1 = new MyUnion2_INT();
34 mu1.i = 30;
35 TestUnion2(mu1, 1);
36
37 MyUnion2_STR mu2 = new MyUnion2_STR();
38 mu2.str = "abc";
39 TestUnion2(mu2, 2);
40 }
41 }
42
43
44
45 /// <summary>
46 /// 如果使用了union中的int字段,就使用这个结构体
47 /// </summary>
48 [StructLayout(LayoutKind.Explicit, Size = 128)]
49 public struct MyUnion2_INT
50 {
51 [FieldOffset(0)]
52 public int i;
53 }
54
55 /// <summary>
56 /// 如果使用了union中的char[]字段,就使用这个结构体
57 /// </summary>
58 [StructLayout(LayoutKind.Sequential)]
59 public struct MyUnion2_STR
60 {
61 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
62 public string str;
63 }
64
65 }
运行结果:

最看再看一下在struct中使用union的情况:
这段示例代码中使用了Windows Data Types(LPWSTR),在不同的平台中,指针的大小不一样,所以在C#中调用时,需要根据平台,定义对应的结构体。
C++
1 struct MYSTRUCTUNION
2 {
3 UINT uType;
4 union
5 {
6 LPWSTR pStr;
7 char cStr[260];
8 };
9 };
10
11 extern "C" __declspec(dllexport) MYSTRUCTUNION GetMyUnion3();
12
13 extern "C" __declspec(dllexport) MYSTRUCTUNION GetMyUnion3()
14 {
15 MYSTRUCTUNION myunion;
16 myunion.uType = 0;
17 myunion.pStr = L"HelloWorld";
18 return myunion;
19 }
C#
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Runtime.InteropServices;
7
8 namespace UnionTest
9 {
10 class Program
11 {
12 [DllImport("UnionLib.dll")]
13 public static extern MyStructUnion GetMyUnion3();
14
15 static void Main(string[] args)
16 {
17 //结构体和联合体一起使用的情况
18 //引用类型部分使用指针再进行转换,直接用字符串将会封送失败
19 var myStructUnion = GetMyUnion3();
20 var str = Marshal.PtrToStringUni(myStructUnion.pStr);
21 Console.WriteLine(str);
22 }
23 }
24
25 #if x86
26 /// <summary>
27 /// 32位
28 /// </summary>
29 [StructLayout(LayoutKind.Explicit, Size = 264)]
30 public struct MyStructUnion
31 {
32 [FieldOffset(0)]
33 public uint uType;
34
35 [FieldOffset(4)]
36 public IntPtr pStr;
37
38 [FieldOffset(4)]
39 public IntPtr cStr;
40 }
41
42 #endif
43
44
45 #if x64
46 /// <summary>
47 /// 64位
48 /// </summary>
49 [StructLayout(LayoutKind.Explicit, Size = 272)]
50 public struct MyStructUnion
51 {
52 [FieldOffset(0)]
53 public uint uType;
54
55 [FieldOffset(8)]
56 public IntPtr pStr;
57
58 [FieldOffset(8)]
59 public IntPtr cStr;
60 }
61 #endif
62
63 }
运行结果:
![]()
更多推荐




所有评论(0)