c# union结构体_P/Invoke各种总结(八、在C#中使用Union联合体)

本文详细介绍了 C# 中如何使用 Union 结构体,包括其特点、与 struct 的区别,以及如何在 C# 中通过 P/Invoke 与 C++ 交互。通过示例展示了如何在 C# 中声明和使用 Union,以及处理包含引用类型的特殊情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Union联合体(共用体)

这里稍微简单介绍一下union这种类型。

有时候需要使几种不同类型的变量存放到同一段内存单元中,例如:可把一个整型变量、一个字符串变量放在同一个地址开始的内存单元中。以上两个变量在内存中占用的字节数不同,但都从同一个地址开始存放,也就是使用覆盖技术,几个变量互相覆盖。这种使用几个不同的变量共同占用同一段内存的结构,称为union。

1 union2 {3 inta;4 charb;5 };

union的特点是:

1、同一个内存段可以用来存放几种不同类型的成员,但在同一时刻只能存放其中一种。

2、union起作用的成员是最后一次存放的变量,在存入一个新的成员后原有的成员就失去作用。

union和struct的区别

struct:所占内存长度是各成员占的内存之和,每个成员分别占用某个自己的内存单元

union:所占内存长度等于最长的成员的长度

在C#中使用union

在C#中与union交互,需要用到StructLayoutAttribute特性和FieldOffsetAttribute特性,关于这两个特性的介绍,可以参考https://www.cnblogs.com/zhaotianff/p/12510286.html

其实在知道union的原理后,在C#中进行映射也是非常简单。

如前面示例代码中的union,在C#中的表示形式如下:

1 [StructLayout(LayoutKind.Explicit)]2 structMyStruct3 {4 [FieldOffset(0)]inta;5 [FieldOffset(0)]charb;6 };

在上一篇文章中,介绍了  StructLayoutAttribute类的Pack字段,这里再介绍一下另外一个字段,StructLayoutAttribute.Size字段,Size字段用于指示类或结构的绝对大小。

在使用union时,如果不是C#中的值类型,就需要指定大小。

测试程序:

最简单的情况:

C++

1 union MYUNION2 {3 intb;4 doubled;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 returnmyunion;14 }

C#

1 usingSystem;2 usingSystem.Collections.Generic;3 usingSystem.Linq;4 usingSystem.Text;5 usingSystem.Threading.Tasks;6 usingSystem.Runtime.InteropServices;7

8 namespaceUnionTest9 {10 classProgram11 {12 [DllImport("UnionLib.dll")]13 public static externMyUnion 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 MYUNION22 {3 inti;4 char str[128];5 };6

7 extern "C" __declspec(dllexport) void TestUnion2(MYUNION2 u, inttype);8

9 extern "C" __declspec(dllexport) void TestUnion2(MYUNION2 u, inttype)10 {11 if (type == 1)12 {13 std::cout << u.i <<:endl else>

16 {17 std::cout << u.str <<:endl>

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 returnmyunion2;28 }

C#

1 usingSystem;2 usingSystem.Collections.Generic;3 usingSystem.Linq;4 usingSystem.Text;5 usingSystem.Threading.Tasks;6 usingSystem.Runtime.InteropServices;7

8 namespaceUnionTest9 {10 classProgram11 {12 [DllImport("UnionLib.dll")]13 public static externIntPtr GetMyUnion2();14

15

16 [DllImport("UnionLib.dll")]17 public static extern void TestUnion2(MyUnion2_INT mm, inti);18

19 [DllImport("UnionLib.dll")]20 public static extern void TestUnion2(MyUnion2_STR mm,inti);21

22 static void Main(string[] args)23 {24 //使用了引用类型无法直接通过签名返回,需要进行转换25 //如果这里是使用了MYUNION2中的i字段,可以直接使用MyUnion2_INT作为返回值

26 var myunion2Ptr =GetMyUnion2();27 MyUnion2_STR unionStr = newMyUnion2_STR();28 unionStr = (MyUnion2_STR)Marshal.PtrToStructure(myunion2Ptr, typeof(MyUnion2_STR));29 Console.WriteLine(unionStr.str);30

31 //注意:值类型和引用类型不允许重叠32 //在使用引用类型的情况下,无法直接返回union

33 MyUnion2_INT mu1 = newMyUnion2_INT();34 mu1.i = 30;35 TestUnion2(mu1, 1);36

37 MyUnion2_STR mu2 = newMyUnion2_STR();38 mu2.str = "abc";39 TestUnion2(mu2, 2);40 }41 }42

43

44

45 ///

46 ///如果使用了union中的int字段,就使用这个结构体47 ///

48 [StructLayout(LayoutKind.Explicit, Size = 128)]49 public structMyUnion2_INT50 {51 [FieldOffset(0)]52 public inti;53 }54

55 ///

56 ///如果使用了union中的char[]字段,就使用这个结构体57 ///

58 [StructLayout(LayoutKind.Sequential)]59 public structMyUnion2_STR60 {61 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]62 public stringstr;63 }64

65 }

运行结果:

最看再看一下在struct中使用union的情况:

这段示例代码中使用了Windows Data Types(LPWSTR),在不同的平台中,指针的大小不一样,所以在C#中调用时,需要根据平台,定义对应的结构体。

C++

1 structMYSTRUCTUNION2 {3 UINT uType;4 union5 {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 returnmyunion;19 }

C#

1 usingSystem;2 usingSystem.Collections.Generic;3 usingSystem.Linq;4 usingSystem.Text;5 usingSystem.Threading.Tasks;6 usingSystem.Runtime.InteropServices;7

8 namespaceUnionTest9 {10 classProgram11 {12 [DllImport("UnionLib.dll")]13 public static externMyStructUnion 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 ///

27 ///32位28 ///

29 [StructLayout(LayoutKind.Explicit, Size = 264)]30 public structMyStructUnion31 {32 [FieldOffset(0)]33 public uintuType;34

35 [FieldOffset(4)]36 publicIntPtr pStr;37

38 [FieldOffset(4)]39 publicIntPtr cStr;40 }41

42 #endif

43

44

45 #if x64

46 ///

47 ///64位48 ///

49 [StructLayout(LayoutKind.Explicit, Size = 272)]50 public structMyStructUnion51 {52 [FieldOffset(0)]53 public uintuType;54

55 [FieldOffset(8)]56 publicIntPtr pStr;57

58 [FieldOffset(8)]59 publicIntPtr cStr;60 }61 #endif

62

63 }

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值