C#實(shí)現(xiàn)Struct結(jié)構(gòu)體與IntPtr轉(zhuǎn)換的示例詳解
在 C# 編程的世界里,我們常常會(huì)遇到需要與非托管代碼交互,或者進(jìn)行一些底層內(nèi)存操作的場(chǎng)景。這時(shí),IntPtr類(lèi)型就顯得尤為重要,它可以表示一個(gè)指針或句柄,用來(lái)指向非托管內(nèi)存中的數(shù)據(jù)。而結(jié)構(gòu)體作為一種常用的數(shù)據(jù)結(jié)構(gòu),在與IntPtr進(jìn)行數(shù)據(jù)傳遞和轉(zhuǎn)換時(shí),往往需要一些繁瑣的操作。為了簡(jiǎn)化這些操作,提高開(kāi)發(fā)效率,我們可以通過(guò)擴(kuò)展方法來(lái)封裝相關(guān)的功能。接下來(lái),就為大家介紹兩段非常實(shí)用的 C# 擴(kuò)展方法代碼,它們實(shí)現(xiàn)了結(jié)構(gòu)體與IntPtr之間的轉(zhuǎn)換等功能。
一、代碼概覽
我們有兩個(gè)擴(kuò)展方法類(lèi),分別是StructExtensions和IntPtrExtensions。StructExtensions類(lèi)主要提供將結(jié)構(gòu)體和結(jié)構(gòu)體數(shù)組轉(zhuǎn)換為IntPtr的方法;IntPtrExtensions類(lèi)則提供了將IntPtr轉(zhuǎn)換回結(jié)構(gòu)體、將IntPtr指向的內(nèi)存數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)組,以及釋放IntPtr所占用的非托管內(nèi)存的方法。
1. StructExtensions 類(lèi)
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);
}
}
在這個(gè)類(lèi)中,第一個(gè)ToIntPtr方法接受一個(gè)結(jié)構(gòu)體類(lèi)型的參數(shù)value,首先使用Marshal.AllocHGlobal方法在非托管內(nèi)存中分配足夠的空間,空間大小由Marshal.SizeOf(value)確定,即結(jié)構(gòu)體實(shí)例的大小。然后通過(guò)Marshal.StructureToPtr方法將結(jié)構(gòu)體實(shí)例的值復(fù)制到分配的非托管內(nèi)存中,并返回指向該內(nèi)存的IntPtr。需要注意的是,使用完返回的IntPtr后,必須釋放其占用的內(nèi)存,否則會(huì)導(dǎo)致內(nèi)存泄漏。
第二個(gè)ToIntPtr方法則是針對(duì)結(jié)構(gòu)體數(shù)組,它根據(jù)數(shù)組的長(zhǎng)度和單個(gè)結(jié)構(gòu)體的大小,在非托管內(nèi)存中分配相應(yīng)大小的空間,并返回指向該內(nèi)存的IntPtr。不過(guò),此方法只是分配了內(nèi)存,并沒(méi)有將數(shù)組中的數(shù)據(jù)復(fù)制到內(nèi)存中,如果需要復(fù)制數(shù)據(jù),還需要進(jìn)一步的操作。
2. IntPtrExtensions 類(lèi)
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類(lèi)中的ToStructure方法,接受一個(gè)IntPtr類(lèi)型的參數(shù)value,通過(guò)Marshal.PtrToStructure方法將IntPtr指向的非托管內(nèi)存中的數(shù)據(jù)轉(zhuǎn)換為指定的結(jié)構(gòu)體類(lèi)型,并返回轉(zhuǎn)換后的結(jié)構(gòu)體實(shí)例。
ToBytes方法將IntPtr指向的內(nèi)存中的數(shù)據(jù)讀取到一個(gè)字節(jié)數(shù)組中。它接受一個(gè)表示內(nèi)存大小的參數(shù)size,首先創(chuàng)建一個(gè)指定大小的字節(jié)數(shù)組,然后使用Marshal.Copy方法將IntPtr指向的內(nèi)存數(shù)據(jù)復(fù)制到字節(jié)數(shù)組中,并返回該字節(jié)數(shù)組。
Free方法用于釋放IntPtr所占用的非托管內(nèi)存,通過(guò)Marshal.FreeHGlobal方法來(lái)實(shí)現(xiàn)內(nèi)存的釋放,由于需要修改IntPtr本身,所以參數(shù)使用了ref關(guān)鍵字。
二、使用示例
下面我們通過(guò)一個(gè)簡(jiǎn)單的示例來(lái)展示這些擴(kuò)展方法的具體使用:
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 };
// 將結(jié)構(gòu)體轉(zhuǎn)換為IntPtr
var intPtr = point.ToIntPtr();
// 將IntPtr轉(zhuǎn)換回結(jié)構(gòu)體
var newPoint = intPtr.ToStructure<Point>();
Console.WriteLine($"X: {newPoint.X}, Y: {newPoint.Y}");
// 釋放IntPtr占用的內(nèi)存
intPtr.Free();
var points = { new Point { X = 1, Y = 2 }, new Point { X = 3, Y = 4 } };
// 將結(jié)構(gòu)體數(shù)組轉(zhuǎn)換為IntPtr
var arrayIntPtr = points.ToIntPtr();
// 這里可以添加將數(shù)組數(shù)據(jù)復(fù)制到內(nèi)存的操作
// 釋放IntPtr占用的內(nèi)存
arrayIntPtr.Free();
}
}
在這個(gè)示例中,我們定義了一個(gè)Point結(jié)構(gòu)體,然后分別演示了將結(jié)構(gòu)體和結(jié)構(gòu)體數(shù)組轉(zhuǎn)換為IntPtr,再將IntPtr轉(zhuǎn)換回結(jié)構(gòu)體,以及釋放IntPtr所占用內(nèi)存的整個(gè)過(guò)程。
三、注意事項(xiàng)
1.內(nèi)存管理:正如前面多次提到的,使用Marshal.AllocHGlobal分配的非托管內(nèi)存必須手動(dòng)釋放,否則會(huì)造成內(nèi)存泄漏。在實(shí)際應(yīng)用中,要確保在合適的時(shí)機(jī)調(diào)用Free方法。
.2數(shù)據(jù)一致性:在將結(jié)構(gòu)體數(shù)組轉(zhuǎn)換為IntPtr時(shí),如果需要將數(shù)組數(shù)據(jù)復(fù)制到內(nèi)存中,需要額外編寫(xiě)代碼實(shí)現(xiàn),否則IntPtr指向的內(nèi)存中數(shù)據(jù)是未初始化的。
3.類(lèi)型安全:在使用ToStructure方法時(shí),要確保IntPtr指向的內(nèi)存中的數(shù)據(jù)與目標(biāo)結(jié)構(gòu)體類(lèi)型一致,否則可能會(huì)導(dǎo)致類(lèi)型轉(zhuǎn)換錯(cuò)誤或程序異常。
通過(guò)這些實(shí)用的擴(kuò)展方法,我們可以更加便捷地在 C# 中處理結(jié)構(gòu)體與IntPtr之間的數(shù)據(jù)轉(zhuǎn)換和內(nèi)存操作,提高代碼的可讀性和可維護(hù)性。
到此這篇關(guān)于C#實(shí)現(xiàn)Struct結(jié)構(gòu)體與IntPtr轉(zhuǎn)換的示例詳解的文章就介紹到這了,更多相關(guān)C# Struct結(jié)構(gòu)體轉(zhuǎn)IntPtr內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#數(shù)據(jù)結(jié)構(gòu)之隊(duì)列(Quene)實(shí)例詳解
這篇文章主要介紹了C#數(shù)據(jù)結(jié)構(gòu)之隊(duì)列(Quene),結(jié)合實(shí)例形式較為詳細(xì)的講述了隊(duì)列的功能、原理與C#實(shí)現(xiàn)隊(duì)列的相關(guān)技巧,需要的朋友可以參考下2015-11-11
C#實(shí)現(xiàn)在PDF文檔中應(yīng)用多種不同字體
在PDF文檔中,可繪制不同字體樣式、不同語(yǔ)言的文字,可通過(guò)使用Standard字體、TrueType字體、CJK字體或者自定義(私有)等字體類(lèi)型。本文將具體介紹實(shí)現(xiàn)的方法,需要的可以參考一下2022-01-01
C#程序員應(yīng)該養(yǎng)成的程序性能優(yōu)化寫(xiě)法
工作和生活中經(jīng)??梢钥吹揭恍┏绦蛟?寫(xiě)代碼的時(shí)候只關(guān)注代碼的邏輯性,而不考慮運(yùn)行效率,其實(shí)這對(duì)大多數(shù)程序猿來(lái)說(shuō)都是沒(méi)有問(wèn)題的,不過(guò)作為一只有理想的CodeMonkey,我還是希望給大家分享一些性能優(yōu)化心得2017-08-08
C#實(shí)現(xiàn)汽車(chē)租賃系統(tǒng)項(xiàng)目
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)汽車(chē)租賃系統(tǒng)項(xiàng)目,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
c# 圓形識(shí)別方案和直線識(shí)別方案的參考示例
這篇文章主要介紹了c# 圓形識(shí)別方案和直線識(shí)別方案的實(shí)現(xiàn)示例,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-03-03
C# 結(jié)合 Javascript 測(cè)試獲取天氣信息
本文將介紹如何使用 C# 并結(jié)合 JavaScript 獲取天氣信息,獲取的數(shù)據(jù)來(lái)源于360瀏覽器首頁(yè)數(shù)據(jù),對(duì)C# 獲取天氣信息示例代碼感興趣的朋友一起看看吧2024-08-08

