C#實現Struct結構體與IntPtr轉換的示例詳解
在 C# 編程的世界里,我們常常會遇到需要與非托管代碼交互,或者進行一些底層內存操作的場景。這時,IntPtr類型就顯得尤為重要,它可以表示一個指針或句柄,用來指向非托管內存中的數據。而結構體作為一種常用的數據結構,在與IntPtr進行數據傳遞和轉換時,往往需要一些繁瑣的操作。為了簡化這些操作,提高開發(fā)效率,我們可以通過擴展方法來封裝相關的功能。接下來,就為大家介紹兩段非常實用的 C# 擴展方法代碼,它們實現了結構體與IntPtr之間的轉換等功能。
一、代碼概覽
我們有兩個擴展方法類,分別是StructExtensions和IntPtrExtensions。StructExtensions類主要提供將結構體和結構體數組轉換為IntPtr的方法;IntPtrExtensions類則提供了將IntPtr轉換回結構體、將IntPtr指向的內存數據轉換為字節(jié)數組,以及釋放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指向的內存中的數據讀取到一個字節(jié)數組中。它接受一個表示內存大小的參數size,首先創(chuàng)建一個指定大小的字節(jié)數組,然后使用Marshal.Copy方法將IntPtr指向的內存數據復制到字節(jié)數組中,并返回該字節(jié)數組。
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內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
c#數據綁定之將datatabel的data添加listView
這篇文章主要介紹了c#將DataTabel的data添加ListView的示例,實現功能是通過響應UI Textbox 的值向ListView 綁定新添加的紀錄。 ,需要的朋友可以參考下2014-04-04C# 使用 OleDbConnection 連接讀取Excel的方法
這篇文章主要介紹了C# 使用 OleDbConnection 連接讀取Excel的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12