Skip to content
Plugins (190)

dotnet

experimental

Typed C# extern functions, async tasks, callbacks and live .NET objects through dotbridge.

use plugin dotnet
6 functions Utilities
Runs with the native Zolo host

Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.

/ filter jk navigate Esc clear
Functions (6)
  1. compile Compile C# once and return a reusable function
  2. compile_file Compile a UTF-8 C# source file
  3. eval Compile and execute C# once
  4. load Load a public method from a .NET DLL
  5. DotNetFunction.call Passes one value; omitted input becomes null.
  6. DotNetFunction.close Releases the managed handle.

From import to result

  1. 01Import only what you use
  2. 02Copy the smallest example
  3. 03Run it with the Zolo CLI

Typed C# functions

use plugin dotnet::{csharp}

extern csharp fn add(a: int, b: int) -> int {
    return a + b;
}

extern csharp async fn twice(value: int) -> int {
    await System.Threading.Tasks.Task.Delay(10);
    return value * 2;
}

print(add(20, 22))
print(await twice(21))

csharp is an imported provider. The function signature uses Zolo types and the body uses C#. Calls behave like ordinary Zolo functions. int is an exact CLR long, i32 is int, and bytes is byte[]. Arrays preserve nil slots and empty sequences; Zolo structs become copied DTOs. Async functions return normal Futures and permit callbacks to reenter Zolo on the owning thread.

Native imports can appear at the beginning of a function or expression block:

extern csharp fn byte_count(text: str) -> int {
    using System.Text;
    return Encoding.UTF8.GetByteCount(text);
}

Namespace imports, aliases and using static use C# syntax and are scoped to the binding. They precede executable statements; resource usings keep their ordinary scope and disposal. Roslyn diagnostics, completion and hover retain the original source positions, including within imports. A using directive selects names from available references; it does not install dependencies.

extern csharp fn apply(value: int, transform: fn(int) -> int) -> int {
    return transform(value);
}
print(apply(21) { |value| value * 2 })

extern csharp fn counter(start: int) -> fn() -> int {
    var count = start;
    return () => System.Threading.Interlocked.Increment(ref count);
}
let next = counter(10)
print(next()) // 11
print(next()) // 12

extern csharp mod declares native classes. extern csharp from imports source, DLL or project types with reflected signatures. Imported objects retain state and identity. Use their original CLR member spelling and dispose resources with defer object.Dispose(). Relative import paths follow the source file. Project imports require an SDK and locked dependencies; inline functions use embedded Roslyn. The runtime requires .NET 8+ and supports VM, native and LLVM targets. Hosted providers produce an explicit unsupported-target error on wasm.

The following dynamic API keeps its original object-based conversion rules.

Compile C# once and return a reusable function

use plugin dotnet
let add = dotnet.compile(r#"async (input) => { return (int)input + 1; }"#)
defer add.close()
print(add.call(41)) // 42

Use raw strings (r#"..."#) to keep C# braces and quotes literal. Accepts an async C# lambda or a public Startup class with Invoke(object). Extra DLL paths participate in compilation and runtime dependency resolution.

Compile a UTF-8 C# source file

Reads UTF-8 C# source and compiles it. Instance fields persist across calls.

Compile and execute C# once

use plugin dotnet
print(dotnet.eval(r#"async (dynamic input) => { return input.name; }"#, #{name: "Zolo"}))

Compiles, calls and releases a function. For repeated calls, use compile.

Load a public method from a .NET DLL

use plugin dotnet
let twice = dotnet.load("./Example.dll", "Example.Startup", "Double")
defer twice.close()
print(twice.call(21))

The public instance or static method accepts one object and returns object or Task<object>. On the VM, paths starting with ./ or ../ follow the caller's source directory; AOT executables use the working directory. An assembly-qualified type name is also accepted.

Passes one value; omitted input becomes null.

Passes one value; omitted input becomes null. Arrays retain empty sequences and nil slots, bytes become byte[], and string-keyed maps become dynamic objects. Calls are serialized and block until C# tasks complete.

Releases the managed handle.

Releases the managed handle. Subsequent calls fail. .NET exceptions, including available stack traces, propagate through Zolo's try/catch channel.

NuGet dependencies for extern C#

use plugin dotnet::{csharp}

extern csharp deps {
    "MathNet.Numerics" = "5.0.0"
}

extern csharp fn factorial(n: int) -> float {
    using MathNet.Numerics;
    return SpecialFunctions.Factorial(checked((int)n));
}

Alternatively, use [foreign.dotnet.dependencies] in zolo.toml, with [packages] schema = 2. zolo install resolves NuGet into an isolated, locked environment before Roslyn compilation. Short strings select exact NuGet versions. Explicit requirement values retain NuGet's native range syntax. Compile references, runtime assemblies and selected native assets are captured in the portable extern artifact.

The default framework is net8.0, with the current host RID. ZOLO_DOTNET_FRAMEWORK and ZOLO_DOTNET_RID select compatible targets. --offline --frozen restores only the matching locked closure from local caches. This dependency workflow applies to typed extern bindings; the legacy DotNetFunction API retains its own dynamic input contract. See foreign dependencies.

Requirements and limits

Build with .NET SDK 8+. Run with the same-architecture .NET runtime 8+. The plugin embeds its managed support libraries and works with VM and native or LLVM executables. CoreCLR is unavailable in browsers and wasm-aot.

Signed 32-bit integers become C# int; larger integers within ±2^53 become double. Larger inputs are rejected; pass strings for long.Parse or decimal.Parse. C# long/ulong results use the upstream double wire format: return strings when exact large values matter. Decimal and Guid return strings; DateTime returns Unix milliseconds. Non-string map keys and callbacks are unsupported.

Set DOTNET_ROOT for custom runtime installations. DOTBRIDGE_BOOTSTRAP_DIR overrides the embedded bootstrap; DOTBRIDGE_DEBUG enables diagnostics.

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en