The LowLevel class provides various helpers for users who are writing
“lower level” code. Calling these methods does not require being inside a
lowlevel context.
The Belte public interface for the String class can be found here.
| Signature | Description |
|---|---|
int! GetHashCode(Object!) |
Equivalent to calling Object.GetHashCode(). |
string! GetTypeName(Object!) |
Equivalent to calling Object.GetTypeName(). |
int! Length<type T>(T!) |
Gets the length of the given array, or 0 if not passed an array. |
void Sort<type T>(T!) |
Sorts the given array, or does nothing if not passed an array. |
int32! SizeOf<type T>() |
Gets the size of the template argument type in number of bytes. |
char* CreateCharPtrString(string!) |
Creates a raw char array with the content of the passed string and returns a pointer to the first element. |
void FreeCharPtrString(char*) |
Frees the memory used by a raw char array*. |
void* GetGCPtr(Object!) |
Creates a garbage collector handle for the given object and returns a pointer to that handle*. |
void FreeGCHandle(void*) |
Frees the given garbage collector handle*. |
Object! GetObject(void*) |
Gets the object associated with the given garbage collector handle. |
string! ReadLPCSTR(void*) |
Creates a string with the contents of a raw null-terminated char array. |
*Note that while you can get the address of this, it is safer to use
GetGCPtr and GetObject as they will stay accurate even if the garbage
collector moves the object, while storing the result of &this can become
stale. In addition, &this points to the start of the object and not the start
of the object’s vtable, meaning calling methods of it will be incorrect unless
the pointer is offset to adjust.
For example:
class MyClass {
int f = 5;
public int GetF() {
return f;
}
public MyClass* GetPtr() {
return &this;
}
}
var myClass = new MyClass();
var ptr = myClass.GetPtr();
// ! Incorrect, undefined behavior
var f = ptr->GetF();
You might think to pin the object and adjust the pointer:
class MyClass {
int f = 5;
public int GetF() {
return f;
}
public MyClass* GetPtr() {
return (MyClass*) (((int64!)&this) + 232);
}
}
pinned var myClass = new MyClass();
var ptr = myClass.GetPtr();
var f = ptr->GetF();
This ensures the object does not move around on the heap, meaning &this will
not be stale. The offset of 232 moves the pointer to the start of the vtable.
However, this should not be done because it relies on the CoreCLR implementation
of objects, which could change meaning the 232 offset becomes invalid. It is
safer to use GetGCPtr and GetObject:
class MyClass {
int f = 5;
public int GetF() {
return f;
}
public void* GetPtr() {
return LowLevel.GetGCPtr(this);
}
}
var myClass = new MyClass();
var ptr = myClass.GetPtr();
var obj = LowLevel.GetObject(ptr);
var f = obj.GetF();
LowLevel.FreeGCHandle(ptr);
Remember to always free the garbage collector handle when finished using it.