C# Binding Conventions
Resources:
- Tracking issue: #48
- .NET native interoperability
- Source-generated P/Invokes
- ClangSharp
The C# binding targets net10.0 and exposes a low-level .NET API over a
generated native import layer.
The .NET 10 LTS baseline gives the binding current source-generated interop tooling, improved code generation for small structs and temporary adapter values, and a clear NativeAOT and trimming compatibility target.
Generate the internal native import layer from the public C headers with ClangSharp. Treat successful generation and compilation as the header bindability check for this path. The public C# layer is handwritten.
Public handle types are classes. They implement IDisposable for deterministic
cleanup and expose explicit Close() or Destroy() methods that report native
status through exceptions. Use finalizers and SafeHandle release paths for
leak reporting. Use them for cleanup only for native resources whose release
function is documented as thread-independent and infallible.
Ordinary calls execute on the calling .NET thread and translate native wrong-thread statuses into exceptions. Managed scheduler and UI dispatch policies belong in adapters above this layer.
Callbacks use static unmanaged thunks with binding-owned callback state stored
through GCHandle or a registry. Use UnmanagedCallersOnly for C-callable
static methods when the signature supports it. Callback state stays live for the
native registration scope and is safe for calls from MapLibre worker, network,
logging, or render-related threads.
Use Span<T>, ReadOnlySpan<T>, NativeMemory, and scoped unsafe blocks for
temporary ABI storage. Represent backend handles with an immutable
NativePointer value around IntPtr.
Publish RID-specific NuGet native assets under runtimes/{rid}/native/, with
managed assemblies under lib or ref for net10.0. Keep package layout
compatible with JIT and NativeAOT consumers.