Code:
class ShapeClass
{
public unsafe Shape* Polygon;
}
struct Shape
{
public unsafe Shape(char* name, int numsides)
{
Sides = numsides;
Name = name;
}
public unsafe char* Name;
public int Sides;
}
private unsafe void MainMethod()
{
char[] name = "PolygonName\0JUNK".ToCharArray();
fixed (char* c = &name[0])
{
Shape shape = new Shape(c, 0);
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Shape)));
Marshal.StructureToPtr(shape, ptr, false);
Shape* s = (Shape*)ptr.ToPointer();
ShapeClass sc = new ShapeClass() {Polygon = s};
Console.WriteLine("[{0}] # Sides: {1}", new string((*sc.Polygon).Name), (*sc.Polygon).Sides);
//Modify the dereferenced pointer
*s = new Shape(c, 5);
Console.WriteLine("[{0}] # Sides: {1}", new string((*sc.Polygon).Name), (*sc.Polygon).Sides);
}
}
Just a quick example on Pointers in C# that I wrote. Here's a cool example actually...
Before we start, you should realize that we can't get a pointer of a reference type, so this wouldn't work exactly with having a pointer to a string in the Shape struct. You could utilize chars though, so that is what we did. We need the string to be null terminated though, so we add \0 to the end manually. You could have your own function do this for you, or have a property that upon the setter appends the null-terminating character to the end as well.
There's lots going on here though... Which makes it somewhat complex, but I think a little example on Pointers though would be useful here.
Alright, to begin my explanation, we have a class called ShapeClass, and a member variable which is a pointer to a Shape structure, and since we're using pointers it must be held under the unsafe keyword context.
Next, we have a Shape struct, and it holds a pointer to a char array called Name, and an integer value for the number of Sides of this shape. It's constructor assigns these values. And just like with the char* variable, it as well must be held under the unsafe context, because we are dealing with pointers.
Note that the pointer to this Shape struct would not be valid if we had any non-blittable types embedded within it. Such as string, which is a reference type. This is the reason why we use char* instead.
Now for the main method, what we are doing is converting a character array "PolygonName" with the null-terminating character manually added to the end, to a char*. And for demonstration purposes, i've added some extra text after the null-terminating character to show you that when converting to char*, the \0 determines where the output cuts off, and the index specified for the address of the index of the array is where we start.
This has to be under the fixed statement and i'll explain why... From MSDN:
The fixed statement sets a pointer to a managed variable and "pins" that variable during the execution of statement. Without fixed, pointers to movable managed variables would be of little use since garbage collection could relocate the variables unpredictably. The C# compiler only lets you assign a pointer to a managed variable in a fixed statement.
This is actually what is called an unfixed expression:
Because it's unfixed, if the garbage collector comes along and cleans things up, the location of this data on the heap could be relocated unpredictably, which causes us to be looking for invalid data in the place where it used to be, if that makes sense. In the fixed statement, it's address is fixed, and therefore pointers to it's address in the heap become even usable.
Inside of the fixed statement, where c becomes our usable char* value... We define a new Shape, assign it to c for it's Name (param char*), and give it a Sides value of 0.
Then we define an IntPtr, and allocate enough space for a type of Shape using Marshal.AllocHGlobal(). Then we use the StructureToPtr() method to assign ptr, the address of our created shape. The last boolean defines whether to delete the old structure, by calling Marshal.DestroyStructure(), which would destroy shape, if set to true.
Now we take this IntPtr, and call the ToPointer() function and cast it to a pointer to a Shape struct (Shape*). Note, you can get away with casting IntPtr to Shape* without calling to ToPointer() function if you want. ToPointer() just converts it to a void* unknown pointer type, which can be cast to Shape* easily after, but either way.
Now here's where the actual demonstration starts in my opinion. We define our instance of ShapeClass() and give it's Polygon the value of s (Shape*). So we're giving it the address of our Shape; a pointer.
This means that the original Shape is not being copied over for it's value, it's being referenced from our original, s.
So we display it's initial values... Change the value of our dereferenced s, and see that our ShapeClass instance reflects those changed values as well.