在 c# 编程的世界里,我们常常会遇到需要与非托管代码交互,或者进行一些底层内存操作的场景。这时,intptr类型就显得尤为重要,它可以表示一个指针或句柄,用来指向非托管内存中的数据。而结构体作为一种常用的数据结构,在与intptr进行数据传递和转换时,往往需要一些繁琐的操作。为了简化这些操作,提高开发效率,我们可以通过扩展方法来封装相关的功能。接下来,就为大家介绍两段非常实用的 c# 扩展方法代码,它们实现了结构体与intptr之间的转换等功能。
一、代码概览
我们有两个扩展方法类,分别是structextensions和intptrextensions。structextensions类主要提供将结构体和结构体数组转换为intptr的方法;intptrextensions类则提供了将intptr转换回结构体、将intptr指向的内存数据转换为字节数组,以及释放intptr所占用的非托管内存的方法。
1. structextensions 类
using system; using system.runtime.interopservices; public static class structextensions { /// <summary> /// struct to intptr /// intptr使用完,需释放 /// </summary> /// <typeparam name="t">struct</typeparam> /// <param name="value">struct值</param> /// <returns>intptr</returns> public static intptr tointptr<t>(this t value) where t : struct { var intptr = marshal.allochglobal(marshal.sizeof(value)); marshal.structuretoptr(value, intptr, true); return intptr; } /// <summary> /// struct[] to intptr /// intptr使用完,需释放 /// </summary> /// <typeparam name="t">struct</typeparam> /// <param name="value">struct[]值</param> /// <returns>intptr</returns> public static intptr tointptr<t>(this t[] value) where t : struct { var intptr = marshal.allochglobal(marshal.sizeof(typeof(t)) * value.length); var longptr = intptr.toint64(); for (var i = 0; i < value.length; i++) { var rectptr = new intptr(longptr); marshal.structuretoptr(value[i], rectptr, false); longptr += marshal.sizeof(typeof(t)); } return new intptr(longptr); } }
在这个类中,第一个tointptr方法接受一个结构体类型的参数value,首先使用marshal.allochglobal方法在非托管内存中分配足够的空间,空间大小由marshal.sizeof(value)确定,即结构体实例的大小。然后通过marshal.structuretoptr方法将结构体实例的值复制到分配的非托管内存中,并返回指向该内存的intptr。需要注意的是,使用完返回的intptr后,必须释放其占用的内存,否则会导致内存泄漏。
第二个tointptr方法则是针对结构体数组,它根据数组的长度和单个结构体的大小,在非托管内存中分配相应大小的空间,并返回指向该内存的intptr。不过,此方法只是分配了内存,并没有将数组中的数据复制到内存中,如果需要复制数据,还需要进一步的操作。
2. intptrextensions 类
using system; using system.runtime.interopservices; public static class intptrextensions { public static t tostructure<t>(this intptr value) where t : struct { return (t)marshal.ptrtostructure(value, typeof(t)); } public static byte[] tobytes(this intptr value, int size) { var bytes = new byte[size]; marshal.copy(value, bytes, 0, size); return bytes; } public static void free(ref this intptr value) { marshal.freehglobal(value); } }
intptrextensions类中的tostructure方法,接受一个intptr类型的参数value,通过marshal.ptrtostructure方法将intptr指向的非托管内存中的数据转换为指定的结构体类型,并返回转换后的结构体实例。
tobytes方法将intptr指向的内存中的数据读取到一个字节数组中。它接受一个表示内存大小的参数size,首先创建一个指定大小的字节数组,然后使用marshal.copy方法将intptr指向的内存数据复制到字节数组中,并返回该字节数组。
free方法用于释放intptr所占用的非托管内存,通过marshal.freehglobal方法来实现内存的释放,由于需要修改intptr本身,所以参数使用了ref关键字。
二、使用示例
下面我们通过一个简单的示例来展示这些扩展方法的具体使用:
using system; using system.runtime.interopservices; struct point { public int x; public int y; } class program { static void main() { var point = new point { x = 10, y = 20 }; // 将结构体转换为intptr var intptr = point.tointptr(); // 将intptr转换回结构体 var newpoint = intptr.tostructure<point>(); console.writeline($"x: {newpoint.x}, y: {newpoint.y}"); // 释放intptr占用的内存 intptr.free(); var points = { new point { x = 1, y = 2 }, new point { x = 3, y = 4 } }; // 将结构体数组转换为intptr var arrayintptr = points.tointptr(); // 这里可以添加将数组数据复制到内存的操作 // 释放intptr占用的内存 arrayintptr.free(); } }
在这个示例中,我们定义了一个point结构体,然后分别演示了将结构体和结构体数组转换为intptr,再将intptr转换回结构体,以及释放intptr所占用内存的整个过程。
三、注意事项
1.内存管理:正如前面多次提到的,使用marshal.allochglobal分配的非托管内存必须手动释放,否则会造成内存泄漏。在实际应用中,要确保在合适的时机调用free方法。
.2数据一致性:在将结构体数组转换为intptr时,如果需要将数组数据复制到内存中,需要额外编写代码实现,否则intptr指向的内存中数据是未初始化的。
3.类型安全:在使用tostructure方法时,要确保intptr指向的内存中的数据与目标结构体类型一致,否则可能会导致类型转换错误或程序异常。
通过这些实用的扩展方法,我们可以更加便捷地在 c# 中处理结构体与intptr之间的数据转换和内存操作,提高代码的可读性和可维护性。
到此这篇关于c#实现struct结构体与intptr转换的示例详解的文章就介绍到这了,更多相关c# struct结构体转intptr内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论