を使用して、固定された配列からMarshal.UnsafeAddrOfPinnedArrayElement
を使用して、配列内の特定の要素のアドレスを取得できます。IntPtr
IntPtrおよびMarshalingコードで使用できるように、固定された配列のラッパーのサンプルクラスを次に示します。
/// <summary>
/// Pins an array of Blittable structs so that we can access the data as bytes. Manages a GCHandle around the array.
/// https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.unsafeaddrofpinnedarrayelement?view=netframework-4.7.2
/// </summary>
public sealed class PinnedArray<T> : IDisposable
{
public GCHandle Handle { get; }
public T[] Array { get; }
public int ByteCount { get; private set; }
public IntPtr Ptr { get; private set; }
public IntPtr ElementPointer(int n)
{
return Marshal.UnsafeAddrOfPinnedArrayElement(Array, n);
}
public PinnedArray(T[] xs)
{
Array = xs;
// This will fail if the underlying type is not Blittable (e.g. not contiguous in memory)
Handle = GCHandle.Alloc(xs, GCHandleType.Pinned);
if (xs.Length != 0)
{
Ptr = ElementPointer(0);
ByteCount = (int) Ptr.Distance(ElementPointer(Array.Length));
}
else
{
Ptr = IntPtr.Zero;
ByteCount = 0;
}
}
void DisposeImplementation()
{
if (Ptr != IntPtr.Zero)
{
Handle.Free();
Ptr = IntPtr.Zero;
ByteCount = 0;
}
}
~PinnedArray()
{
DisposeImplementation();
}
public void Dispose()
{
DisposeImplementation();
GC.SuppressFinalize(this);
}
}
IMHO PInvokeおよびIntPtrの操作は、アセンブリを安全でないものとしてマークし、安全でないコンテキストでポインターを使用するのと同じくらい危険です(それ以上ではない場合)
安全でないブロックを気にしない場合は、IntPtr
キャストを操作する拡張関数をbyte*
次のように記述できます。
public static long Distance(this IntPtr a, IntPtr b)
{
return Math.Abs(((byte*)b) - ((byte*)a));
}
ただし、いつものように、さまざまなポインタタイプにキャストする場合は、配置の問題が発生する可能性があることに注意する必要があります。