C#中Struct与IntPtr转换:实用扩展方法

C#中Struct与IntPtr转换:实用扩展方法

在 C# 编程的世界里,我们常常会遇到需要与非托管代码交互,或者进行一些底层内存操作的场景。这时,IntPtr类型就显得尤为重要,它可以表示一个指针或句柄,用来指向非托管内存中的数据。而结构体作为一种常用的数据结构,在与IntPtr进行数据传递和转换时,往往需要一些繁琐的操作。为了简化这些操作,提高开发效率,我们可以通过扩展方法来封装相关的功能。接下来,就为大家介绍两段非常实用的 C# 扩展方法代码,它们实现了结构体与IntPtr之间的转换等功能。

一、代码概览

我们有两个扩展方法类,分别是StructExtensionsIntPtrExtensionsStructExtensions类主要提供将结构体和结构体数组转换为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 };// 将结构体转换为IntPtrvar 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 } };// 将结构体数组转换为IntPtrvar arrayIntPtr = points.ToIntPtr();// 这里可以添加将数组数据复制到内存的操作// 释放IntPtr占用的内存arrayIntPtr.Free();}}

在这个示例中,我们定义了一个Point结构体,然后分别演示了将结构体和结构体数组转换为IntPtr,再将IntPtr转换回结构体,以及释放IntPtr所占用内存的整个过程。

三、注意事项

    1. 内存管理:正如前面多次提到的,使用Marshal.AllocHGlobal分配的非托管内存必须手动释放,否则会造成内存泄漏。在实际应用中,要确保在合适的时机调用Free方法。
    1. 数据一致性:在将结构体数组转换为IntPtr时,如果需要将数组数据复制到内存中,需要额外编写代码实现,否则IntPtr指向的内存中数据是未初始化的。
    1. 类型安全:在使用ToStructure方法时,要确保IntPtr指向的内存中的数据与目标结构体类型一致,否则可能会导致类型转换错误或程序异常。

通过这些实用的扩展方法,我们可以更加便捷地在 C# 中处理结构体与IntPtr之间的数据转换和内存操作,提高代码的可读性和可维护性。希望本文对你在 C# 编程中处理相关问题有所帮助,如果你在使用过程中有任何疑问或遇到其他问题,欢迎在评论区交流讨论。

以上博客介绍了代码的功能和使用要点。你若觉得内容还需补充,比如增加更多示例或详细解释某个部分,欢迎告诉我。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/83664.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机归属地查询接口如何用Java调用?

一、什么是手机归属地查询接口&#xff1f; 是一种便捷、高效的工具&#xff0c;操作简单&#xff0c;请求速度快。它不仅能够提高用户填写地址的效率&#xff0c;还能帮助企业更好地了解客户需求&#xff0c;制定个性化的营销策略&#xff0c;降低风险。随着移动互联网的发展…

43、视图解析-Thymeleaf初体验

43、视图解析-Thymeleaf初体验 “43、视图解析-Thymeleaf初体验”通常是指在学习Spring Boot框架时&#xff0c;关于如何使用Thymeleaf模板引擎进行视图解析的入门课程或章节。以下是对该主题的详细介绍&#xff1a; #### Thymeleaf简介 - **定义**&#xff1a;Thymeleaf是一个…

Day 40训练

Day 40 训练 PyTorch 图像数据训练与测试的规范写法单通道图像的规范训练流程数据预处理与加载模型定义训练与测试函数封装模型训练执行 彩色图像的扩展应用数据预处理调整模型结构调整 关键要点总结 知识点回顾&#xff1a; 彩色和灰度图片测试和训练的规范写法&#xff1a;封…

杰理可视化SDK--系统死机异常调试

杰理可视化SDK--系统死机异常调试 系统异常原因杰理SDK异常调试准备工作杰理SDK系统异常定位异常代码示例1异常代码示例2 在使用杰理可视化SDK进行软件开发时&#xff0c;往往会遇到一些系统异常问题&#xff0c;系统异常是指芯片在运行代码时&#xff0c;由于软件或硬件状态出…

图简记。。

模仿&#xff1a; algorithm-journey/src/class059/Code01_CreateGraph.java at main algorithmzuo/algorithm-journey Code01_CreateGraph C语言&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h>#define MAXN 11 #define MAX…

Linux 常用命令与 Shell 简介

文章目录 **Linux 常用命令与 Shell 简介****Shell 简介****什么是 Shell&#xff1f;****Shell 的工作原理****常见 Shell 类型****命令行基础****Tab 补全与通配符** **Linux 常用命令****1. 入门必备命令****1.1 寻求帮助 - man 命令****1.2 用户间切换 - su 命令****1.3 特…

基于51单片机的超声波智能避障小车仿真

目录 具体实现功能 设计介绍 资料内容 全部内容 资料获取 具体实现功能 &#xff08;1&#xff09;超声波实时测量小车与障碍物间的距离&#xff0c;并用LCD1602显示。 &#xff08;2&#xff09;当测得的距离超过50时&#xff0c;前进电机转动&#xff08;模拟后轮&#…

AIGC工具平台-GPT-SoVITS-v4-TTS音频推理克隆

声音克隆与语音合成的结合&#xff0c;是近年来生成式AI在多模态方向上的重要落地场景之一。随着预训练模型能力的增强&#xff0c;结合语音识别、音素映射与TTS合成的端到端系统成为初学者可以上手实践的全流程方案。 围绕 GPT-SoVITS-v4-TTS 模块&#xff0c;介绍了其在整合…

Android7 Input(十)View 处理Input事件pipeline

概述: 本文主要描述View对InputEvent事件pipeline处理过程。 本文涉及的源码路径 frameworks/base/core/java/android/view/ViewRootImpl.java InputEvent事件处理 View处理input事件是调用doProcessInputEvents方法&#xff0c;如下所示: void doProcessInputEvents() {//…

Neo4j 完全指南:从入门到精通

第1章&#xff1a;Neo4j简介与图数据库基础 1.1 图数据库概述 传统关系型数据库与图数据库的对比图数据库的核心优势图数据库的应用场景 1.2 Neo4j的发展历史 Neo4j的起源与演进Neo4j的版本迭代Neo4j在图数据库领域的地位 1.3 图数据库的基本概念 节点(Node)与关系(Relat…

网心云 OEC/OECT 笔记(1) 拆机刷入Armbian固件

目录 网心云 OEC/OECT 笔记(1) 拆机刷入Armbian固件网心云 OEC/OECT 笔记(2) 运行RKNN程序 外观 内部 PCB正面 PCB背面 PCB背面 RK3566 1Gbps PHY 配置 OEC 和 OECT(OEC-turbo) 都是基于瑞芯微 RK3566/RK3568 的网络盒子, 没有HDMI输入输出. 硬件上 OEC 和 OECT…

摄像机ISP处理流程

1.Bayer&#xff1a;生成raw图&#xff0c;添加色彩数据&#xff08;RGB&#xff09;&#xff0c;一般会将G的占比设置为R和B的和&#xff0c;实例&#xff1a; 2.黑电平矫正&#xff1a;减去暗电流造成的误差&#xff1b; 3.镜头矫正&#xff1a;对四周的亮度进行矫正&#x…

【后端架构师的发展路线】

后端架构师的发展路线是从基础开发到技术领导的系统性进阶过程&#xff0c;需融合技术深度、架构思维和业务洞察力。以下是基于行业实践的职业发展路径和关键能力模型&#xff1a; 一、职业发展阶梯‌ 初级工程师&#xff08;1-3年&#xff09;‌ 核心能力‌&#xff1a;掌…

Unity VR/MR开发-VR开发与传统3D开发的差异

视频讲解链接&#xff1a;【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili

RabbitMQ如何保证消息可靠性

RabbitMQ是一个流行的开源消息代理&#xff0c;它提供了可靠的消息传递机制&#xff0c;广泛应用于分布式系统和微服务架构中。在现代应用中&#xff0c;确保消息的可靠性至关重要&#xff0c;以防止消息丢失和重复处理。本文将详细探讨RabbitMQ如何通过多种机制保证消息的可靠…

批量图片管理软件介绍

软件介绍 本文介绍一款功能全面的图片处理软件 - FastStone Image Viewer。 软件功能概述 FastStone Image Viewer不仅支持图片查看&#xff0c;还具备编辑、批量重命名和批量转换等多种实用功能。 用户授权说明 该软件对个人用户完全免费&#xff0c;企业用户只需输入用户…

Playwright 测试框架 - Java

🚀【Playwright + Java 实战教程】从零到一掌握自动化测试利器! 🔧 本文专为 Java 开发者量身打造,通过详尽示例带你快速掌握 Playwright 自动化测试。涵盖基础操作、表单交互、测试框架集成、高阶功能及常见实战技巧,适用于企业 UI 测试与 CI/CD 场景。 🛠️ 一、环境…

nvidia系列教程-Usb otg模式修改为host模式

目录 前言 一、了解 USB OTG 模式与 Host 模式 二、host模式切换 总结 前言 在 NVIDIA 设备的使用过程中,有时我们需要将 USB OTG(On-The-Go)模式切换为 Host 模式,以满足连接外部设备(如 U 盘、鼠标、键盘等)的需求。本文将详细介绍如何在 NVIDIA 设备上进行这一模式…

二叉树-104.二叉树的最大深度-力扣(LeetCode)

一、题目解析 这里需要注意根节点的深度是1&#xff0c;也就是说计算深度的是从1开始计算的 二、算法原理 解法1&#xff1a;广度搜索&#xff0c;使用队列 解法2&#xff1a;深度搜索&#xff0c;使用递归 当计算出左子树的深度l&#xff0c;与右子树的深度r时&#xff0c;…

Calendar类日期设置进位问题

背景 报表需求&#xff0c;需要传递每组数据中最小的日期&#xff0c;后台根据传递的最小日期&#xff0c;向前取参数传递的月份的上个月为结束时间的近五个月数据 例&#xff1a;参数传:2025/02&#xff0c;则需返回2025/01, 2024/12, 2024/11, 2024/10, 2024/09这五个年月数据…