With each release of C#, it gains more low-level capabilities. While not useful to most business application developers, these features allow for high performance code suitable for graphics processing, machine learning, and mathematical packages.
In these next two proposals, we see new ways to reference and invoke functions.
Static Delegates
A normal delegate in C# is a somewhat complex data structure. It contains a function pointer, an optional object reference for the this parameter, and a link to a chained delegate. The whole structure is heap-allocated like any other reference type, with the corresponding memory pressure. Furthermore, whenever it is used with unmanaged code it needs to be marshalled.
Compared to a normal delegate, a “static delegate” is greatly simplified. It is implemented as a struct with one field, a function pointer of type IntPtr. This makes it blittable, which means it has exactly the same memory layout when used in managed and unmanaged code. Or in other words, marshalling isn’t required when calling a native system function.
It is declared using this syntax:
static delegate int Func()
Additional settings such as character set and calling convention can be specified using an attribute similar to the UnmanagedFunctionPointer attribute.
Static delegates are not without limitations. They can only refer to static functions; member methods on objects are not permitted because there is no place to store the pointer to the object. Furthermore, static delegates cannot be chained to other delegates.
At the CLR level, a static delegate is invoked using the calli (call indirect) instruction. By contrast, a normal delegate is invoked using the call or callvirt (call virtual) instruction.
In order to make it more compatible with existing code, an implicit conversion from a static delegate to a normal delegate would be allowed. Going the other direction would require an explicit cast, as not all normal delegates meet the requirements of a static delegate.
You can read more about the Static Delegates proposal on GitHub.
Function Pointers
A competing proposal is simply titled Function Pointers. This too exposes the ability to use the calli instruction and its partner, the ldftn (load method pointer) instruction. Like the previous proposal, it begins with a delegate declaration. Though in this case, the keyword delegate is replaced with funcptr.
funcptr int F1(int value);
When invoking a native function, the caller needs to know what the calling convention is. This affects things such as the order of items on the stack and whether the caller or callee is responsible for clearing the stack after use. The calling conventions covered by this proposal are cdecl, fastcall, stdcall, thiscall or winapi. The developer can specify which to use by modifying the delegate’s declaration:
funcptr cdecl int F1(int value);
In this proposal, function pointers would only be allowed in an unsafe context.
As part of this proposal you would be able to use the address-of operator (&) on a function’s name in order to create a function pointer. This too is only allowed to be used in an unsafe context.
The other restrictions on a function pointer are the same as a static delegate. Specifically, they can only refer to static functions and cannot be chained to other delegates.
Both proposals are under consideration and are not currently on the C# road map. A third proposal titled Compiler Intrinsics also competes with these two, but has additional limitations that make it less compelling.