Implementing a generic unmanaged array in C#
I'm implementing an unmanaged array class in C# which I need for some OpenGL calls.
It's going great, but I've hit a roadblock. The following code doesn't compile, and I understand why, but how can I make it work?
public T this[int i]
{
get { return *((T*)arrayPtr + i); }
set { *((T*)arrayPtr + i) = value; }
}
I thought that it might work if I ensured that T is a struct
unsafe class FixedArray<T> where T : struct
Doesn't work either...
How can I get something functionally equivilant to what I'm trying to do above?
EDIT: I'm using an unmanaged array with Marshal.AllocHGlobal() so that my array is fixed and the GC won't move it around. OpenGL doesn't actually process the instructions when you call it, OpenGL will try to access the array long after the function has returned.
Here's the whole class if that helps:
unsafe class FixedAr开发者_StackOverflowray<T> where T : struct
{
IntPtr arrayPtr;
public T this[int i]
{
get { return *((T*)arrayPtr + i); }
set { *((T*)arrayPtr + i) = value; }
}
public FixedArray(int length)
{
arrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)) * length);
}
~FixedArray()
{
Marshal.FreeHGlobal(arrayPtr);
}
}
The error message is Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
I'm pretty sure that getting your code to compile isn't possible. For your code (T*)arrayPtr + i
, this would compute arrayPtr + i * 4
when T
is an int
and arrayPtr + i * 8
when T
is a long
. Since the CLR has no way of substituting a 4, 8, or whatever sizeof(T)
happens to be, this code just can't work.
What you need to do is use Marshal
to do all the work for you. This compiles, but I haven't tested it:
unsafe class FixedArray<T> where T : struct
{
IntPtr arrayPtr;
int sizeofT { get { return Marshal.SizeOf(typeof(T)); } }
public T this[int i]
{
get
{
return (T)Marshal.PtrToStructure(arrayPtr + i * sizeofT, typeof(T));
}
set
{
Marshal.StructureToPtr(value, arrayPtr + i * sizeofT, false);
}
}
public FixedArray(int length)
{
arrayPtr = Marshal.AllocHGlobal(sizeofT * length);
}
~FixedArray()
{
Marshal.FreeHGlobal(arrayPtr);
}
}
You don't need an unmanaged array.
You can declare a managed array normally:
var myArray = new MyStruct[13];
and then use the GCHandle
class to pin it in memory and get its address:
var handle = GCHandle.Alloc(myArray, GCHandleType.Pinned);
IntPtr address = handle.AddrOfPinnedObject();
The array will exist at that memory address until you call handle.Free
. If you never call Free
, then it will stay there until your program exits.
It is now possible in c# 7.3. You need to specify the unmanaged
generic constraint:
unsafe class FixedArray<T> where T : unmanaged
{
T* arrayPtr;
public T this[int i]
{
get { return *(arrayPtr + i); }
set { *(arrayPtr + i) = value; }
}
public FixedArray(int length)
{
arrayPtr = (T*)Marshal.AllocHGlobal(sizeof(T) * length);
}
~FixedArray()
{
Marshal.FreeHGlobal((IntPtr)arrayPtr);
}
}
Unfortunately there are still limitations. For instance, the type T
cannot contain generic types, so it is not possible [at the moment] for FixedArray
to contain other FixedArray
s.
精彩评论