Here William Acton, Director at Firefinch Software, reviews the new native AOT feature in .NET 7 and how it makes it feasible to write a library in C# and use it from any other language.
For those who just want to see code, we have a repository that demonstrates compiling a native C# DLL and using it from Python, Java, Rust, JavaScript (and more!): https://github.com/waacton/Native-CSharp
Scenario
Imagine you’ve got a carefully crafted and extensively tested library written in C#, but you want to make the logic in the library available to the Python and Java communities. How would you do that?
Existing approaches
The obvious route is to translate the library into Python and Java, but now you’ve got 3 codebases to maintain whenever bugs are fixed or features are added. And how confident would you be that the code is good quality if it’s not your preferred language?
An alternative route could be to expose the library functions via a small local web server, where Python and Java users access them through the HTTP tools provided by the languages. This removes the need for translating the library into other languages, however, it comes with a price. Not only does the user need to fire up a service alongside their own application, it would tie them into requiring a .NET runtime installation to run the service. Although, this might be less of an issue in the upcoming .NET 8.
The ‘Native AOT’ approach
With Native AOT, a new option is available. You can compile the library to native code and expose the functions to “unmanaged callers only“. This effectively creates entry points into the DLL that can be executed without a .NET runtime installed, much like a standard C library.
As a result, any language that supports calling functions in an external C library should also be able to call functions in the natively compiled C# library! Here’s a demo with some code examples: https://github.com/waacton/Native-CSharp.
Considerations
There are, of course, limitations. Exposed functions are restricted to using blittable types for arguments, and you have to be mindful of other low-level concerns such as pointers, memory allocation and garbage collection. Lastly, some .NET functionality is not compatible.
Our round-up
The native AOT approach comes with its own set of challenges and considerations of suitability. However, it looks to be a great way to enable interoperability for low-level behaviour, such as instrument drivers, with the power of a modern high-level language like C#.