C# - Function Pointers
In C#, function pointers are a feature introduced in C# 9.0, which allow you to work with unmanaged function pointers (similar to pointers in languages like C or C++). They enable direct invocation of methods through memory addresses, providing more control and efficiency, particularly useful in scenarios where you need low-level memory manipulation, performance optimizations, or interoperability with unmanaged code.
Key Features of Function Pointers:
- Unmanaged Context: Function pointers must be used in an
unsafe
context because they deal directly with memory, which bypasses some safety checks in managed code. - High Performance: Function pointers allow for fast invocation of functions, avoiding the overhead of delegate invocation or virtual method calls.
- Interoperability: Useful when interacting with native libraries, such as through P/Invoke or calling unmanaged C/C++ functions.
Declaring a Function Pointer:
To declare and use a function pointer, you need to use the delegate*
keyword in an unsafe
block.
Syntax:
csharpunsafe delegate*<ParameterTypes, ReturnType> functionPointer;
delegate*
declares the function pointer.<ParameterTypes, ReturnType>
specifies the types of parameters the function takes and the return type.
Example of Function Pointer Declaration:
csharpunsafe
{
// Declare a function pointer that takes two integers and returns an integer
delegate*<int, int, int> addPointer;
// Assign the address of a function to the function pointer
addPointer = &Add;
// Use the function pointer to call the method
int result = addPointer(10, 20);
Console.WriteLine(result); // Output: 30
}
// Regular method to be used with a function pointer
static int Add(int a, int b)
{
return a + b;
}
Calling Convention:
You can also specify a calling convention when working with unmanaged function pointers. This tells the runtime how the arguments should be passed to the function and how the return value should be handled.
csharpunsafe delegate* unmanaged[Cdecl]<int, int, int> addPointer;
unmanaged[Cdecl]
specifies the calling convention (in this case,Cdecl
, commonly used in C/C++).- Other calling conventions include
Stdcall
,Thiscall
, andFastcall
.
Example with Interop (Calling a Native Function):
csharp[DllImport("SomeNativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern unsafe delegate*<int, int, int> GetAddFunctionPointer();
unsafe
{
// Retrieve the function pointer from a native library
delegate*<int, int, int> addPointer = GetAddFunctionPointer();
// Use the function pointer to call the native function
int result = addPointer(10, 20);
Console.WriteLine(result); // Calls the native add function
}
Key Points:
- Unsafe Context: Function pointers require the
unsafe
keyword because they bypass many of C#'s managed runtime features (like garbage collection safety). - No Garbage Collection: Function pointers are unmanaged, meaning they are not tracked by the garbage collector, so you need to manage memory carefully.
- Performance: They offer a more performant alternative to delegates or virtual method calls in certain scenarios.
- Interoperability: Function pointers are ideal for use cases where you need to interact with unmanaged code, such as calling functions from native libraries.