Binding · C# / .NET

C# / .NET — in 5 Minuten zum ersten Sample

.NET 8. P/Invoke gegen die C-API in libzerodds. Idiomatic C# (records, async/await, IDisposable). IDL→C#-Codegen via zerodds-idlc generate --lang csharp; der erzeugte Code ruft den XCDR2-Codec in ZeroDds.Wire.

.NET: 8 (LTS) Install: dotnet add package ZeroDDS --prerelease

5-Minuten-Quickstart

libzerodds als Native-Dep, NuGet-Package, ~20 Zeilen C#.

1 · Native-Lib + C#-Binding installieren

Das Binding kommt von NuGet (ZeroDDS, mit ZeroDDS.Cdr und ZeroDds.Wire). Die native Bibliothek libzerodds ist nicht im Paket; sie kommt aus den Systempaketen oder aus dem Source.

Schritt 1a: libzerodds Runtime holen — pre-built oder Source:

# Ubuntu/Debian (amd64; arm64: _arm64.deb)
wget https://github.com/zero-objects/zero-dds/releases/download/v1.0.0-rc.7/zerodds-core_1.0.0-rc.7_amd64.deb
sudo dpkg -i zerodds-core_1.0.0-rc.7_amd64.deb

# Fedora/RHEL: die RPMs von rc.7 lassen sich nicht installieren (zerodds-core verlangt libzerodds.so()(64bit), das kein Paket bereitstellt); aus der Quelle bauen, siehe User Guide

# Arch:  yay -S zerodds-bin
# macOS, Windows: libzerodds ist in keinem vorgebauten Paket (.pkg, MSI und Zip enthalten nur die Programme); aus dem Source bauen (unten)

# Aus Source (Entwicklung):
git clone https://github.com/zero-objects/zero-dds.git
cd zero-dds && cargo build --release -p zerodds-c-api

Schritt 1b: Projekt anlegen, C#-Binding von NuGet:

dotnet new console -o zerodds-hello
cd zerodds-hello
dotnet add package ZeroDDS --version 1.0.0-rc.7

2 · Pub/Sub-Roundtrip (Program.cs)

using ZeroDDS;

var factory = DomainParticipantFactory.Instance;
var participant = factory.CreateParticipant(0);

var topic = participant.CreateBytesTopic("Chatter");
var publisher = participant.CreatePublisher();
var subscriber = participant.CreateSubscriber();

using var writer = publisher.CreateBytesWriter(topic);
using var reader = subscriber.CreateBytesReader(topic);

writer.WaitForMatchedSubscription(1, TimeSpan.FromSeconds(5));
reader.WaitForMatchedPublication(1, TimeSpan.FromSeconds(5));

writer.Write("hello"u8.ToArray());
reader.WaitForData(TimeSpan.FromSeconds(3));
foreach (var payload in reader.Take())
{
    Console.WriteLine($"got: {System.Text.Encoding.UTF8.GetString(payload)}");
}

▶ Runnable example: csharp-quickstart

3 · Laufen

dotnet run
# got: hello

Installation

Über NuGet

dotnet add package ZeroDDS --version 1.0.0-rc.7
# zieht ZeroDDS.Cdr (Vertrag der erzeugten TypeSupport-Klassen) und ZeroDds.Wire (XCDR2-Codec) mit

Über Source

git clone https://github.com/zero-objects/zero-dds.git
cd zero-dds/crates/cs/csharp
dotnet build ZeroDDS/ZeroDDS.csproj -c Release
# DLL unter ZeroDDS/bin/Release/net8.0/
# im eigenen Projekt: dotnet add reference path/to/zero-dds/crates/cs/csharp/ZeroDDS/ZeroDDS.csproj

Native-Lib finden

libzerodds.{so,dylib,dll} muss zur Laufzeit auffindbar sein. Drei Optionen:

  • System-Install (/usr/lib/libzerodds.so via DEB/RPM)
  • LD_LIBRARY_PATH=/path/to/lib dotnet run
  • Neben das Binary kopieren (bin/Release/net8.0/libzerodds.so)

Typisierte Topics — zerodds-idlc generate --lang csharp

IDL (temperature.idl)

module sensor_msgs {
    module msg {
        struct Temperature {
            long celsius;
            string sensor_id;
        };
    };
};

Codegen + Use

zerodds-idlc generate --lang csharp --out Gen/ temperature.idl
# schreibt Gen/temperature.cs und Gen/temperature.TypeSupport.cs

// Nutzen
using sensor_msgs.msg;

var topic = participant.CreateTypedTopic<Temperature>("Temp");
var writer = publisher.CreateTypedWriter<Temperature>(topic);
writer.Write(new Temperature { Celsius = 23, Sensor_id = "A7" });

▶ Runnable example: csharp-typed

Neben dem Typ schreibt zerodds-idlc die Klasse TemperatureTypeSupport, eine IDdsTopicType<Temperature> aus ZeroDDS.Cdr; sie ruft den XCDR2-Codec in ZeroDds.Wire. Der IDL-Member sensor_id heißt dort Sensor_id. CreateTypedTopic<T>(name) findet die Klasse über ihren Namen <T>TypeSupport; CreateTypedTopic(name, TemperatureTypeSupport.Instance) übergibt sie direkt.

async/await

await writer.WriteAsync(payload, ct);
await reader.WaitForDataAsync(TimeSpan.FromSeconds(3), ct);
await foreach (var sample in reader.TakeAsync(ct))
{
    Console.WriteLine(sample);
}

▶ Runnable example: csharp-async

Cancellation-Token wird durchgereicht; OperationCanceledException beim Cancel.

NativeAOT

Die P/Invoke-Schicht ist für NativeAOT geschrieben: Jeder DllImport nennt seinen EntryPoint, ohne Reflection (Native.cs), und die Listener-Callbacks sind [UnmanagedCallersOnly]-Methoden ohne Delegate-Marshalling (Listener.cs).

CreateTypedTopic<T>(name) sucht die TypeSupport-Klasse dagegen per Reflection. Wo Reflection nicht zur Verfügung steht, übergibt man sie direkt:

var topic = participant.CreateTypedTopic("Temp", TemperatureTypeSupport.Instance);
Binding · C# / .NET

C# / .NET — first sample in 5 minutes

.NET 8. P/Invoke against the C API in libzerodds. Idiomatic C# (records, async/await, IDisposable). IDL→C# codegen via zerodds-idlc generate --lang csharp; the generated code calls the XCDR2 codec in ZeroDds.Wire.

.NET: 8 (LTS) Install: dotnet add package ZeroDDS --prerelease

5-minute quickstart

libzerodds as a native dep, the NuGet package, ~20 lines of C#.

1 · Install the native lib + C# binding

The binding comes from NuGet (ZeroDDS, with ZeroDDS.Cdr and ZeroDds.Wire). The native library libzerodds is not in the package; it comes from the system packages or from source.

Step 1a: get the libzerodds runtime — pre-built or source:

# Ubuntu/Debian (amd64; arm64: _arm64.deb)
wget https://github.com/zero-objects/zero-dds/releases/download/v1.0.0-rc.7/zerodds-core_1.0.0-rc.7_amd64.deb
sudo dpkg -i zerodds-core_1.0.0-rc.7_amd64.deb

# Fedora/RHEL: the rc.7 RPMs cannot be installed (zerodds-core requires libzerodds.so()(64bit), which no package provides); build from source, see the User Guide

# Arch:  yay -S zerodds-bin
# macOS, Windows: libzerodds is in no pre-built package (.pkg, MSI and zip carry only the programs); build it from source (below)

# From source (development):
git clone https://github.com/zero-objects/zero-dds.git
cd zero-dds && cargo build --release -p zerodds-c-api

Step 1b: create the project, the C# binding from NuGet:

dotnet new console -o zerodds-hello
cd zerodds-hello
dotnet add package ZeroDDS --version 1.0.0-rc.7

2 · Pub/sub roundtrip (Program.cs)

using ZeroDDS;

var factory = DomainParticipantFactory.Instance;
var participant = factory.CreateParticipant(0);

var topic = participant.CreateBytesTopic("Chatter");
var publisher = participant.CreatePublisher();
var subscriber = participant.CreateSubscriber();

using var writer = publisher.CreateBytesWriter(topic);
using var reader = subscriber.CreateBytesReader(topic);

writer.WaitForMatchedSubscription(1, TimeSpan.FromSeconds(5));
reader.WaitForMatchedPublication(1, TimeSpan.FromSeconds(5));

writer.Write("hello"u8.ToArray());
reader.WaitForData(TimeSpan.FromSeconds(3));
foreach (var payload in reader.Take())
{
    Console.WriteLine($"got: {System.Text.Encoding.UTF8.GetString(payload)}");
}

▶ Runnable example: csharp-quickstart

3 · Run

dotnet run
# got: hello

Installation

Via NuGet

dotnet add package ZeroDDS --version 1.0.0-rc.7
# pulls in ZeroDDS.Cdr (the contract of the generated TypeSupport classes) and ZeroDds.Wire (the XCDR2 codec)

Via source

git clone https://github.com/zero-objects/zero-dds.git
cd zero-dds/crates/cs/csharp
dotnet build ZeroDDS/ZeroDDS.csproj -c Release
# DLL under ZeroDDS/bin/Release/net8.0/
# in your own project: dotnet add reference path/to/zero-dds/crates/cs/csharp/ZeroDDS/ZeroDDS.csproj

Finding the native lib

libzerodds.{so,dylib,dll} must be discoverable at runtime. Three options:

  • System install (/usr/lib/libzerodds.so via DEB/RPM)
  • LD_LIBRARY_PATH=/path/to/lib dotnet run
  • copy it next to the binary (bin/Release/net8.0/libzerodds.so)

Typed topics — zerodds-idlc generate --lang csharp

IDL (temperature.idl)

module sensor_msgs {
    module msg {
        struct Temperature {
            long celsius;
            string sensor_id;
        };
    };
};

Codegen + use

zerodds-idlc generate --lang csharp --out Gen/ temperature.idl
# writes Gen/temperature.cs and Gen/temperature.TypeSupport.cs

// use it
using sensor_msgs.msg;

var topic = participant.CreateTypedTopic<Temperature>("Temp");
var writer = publisher.CreateTypedWriter<Temperature>(topic);
writer.Write(new Temperature { Celsius = 23, Sensor_id = "A7" });

▶ Runnable example: csharp-typed

Beside the type, zerodds-idlc writes the class TemperatureTypeSupport, an IDdsTopicType<Temperature> from ZeroDDS.Cdr; it calls the XCDR2 codec in ZeroDds.Wire. The IDL member sensor_id is Sensor_id there. CreateTypedTopic<T>(name) finds the class by its name <T>TypeSupport; CreateTypedTopic(name, TemperatureTypeSupport.Instance) passes it directly.

async/await

await writer.WriteAsync(payload, ct);
await reader.WaitForDataAsync(TimeSpan.FromSeconds(3), ct);
await foreach (var sample in reader.TakeAsync(ct))
{
    Console.WriteLine(sample);
}

▶ Runnable example: csharp-async

The cancellation token is threaded through; OperationCanceledException on cancel.

NativeAOT

The P/Invoke layer is written for NativeAOT: every DllImport names its EntryPoint, with no reflection (Native.cs), and the listener callbacks are [UnmanagedCallersOnly] methods with no delegate marshalling (Listener.cs).

CreateTypedTopic<T>(name), by contrast, looks the TypeSupport class up by reflection. Where reflection is not available, pass it directly:

var topic = participant.CreateTypedTopic("Temp", TemperatureTypeSupport.Instance);