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

