Ranvier.CSharp 0.1.1-preview.1

This is a prerelease version of Ranvier.CSharp.
dotnet add package Ranvier.CSharp --version 0.1.1-preview.1
                    
NuGet\Install-Package Ranvier.CSharp -Version 0.1.1-preview.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Ranvier.CSharp" Version="0.1.1-preview.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ranvier.CSharp" Version="0.1.1-preview.1" />
                    
Directory.Packages.props
<PackageReference Include="Ranvier.CSharp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Ranvier.CSharp --version 0.1.1-preview.1
                    
#r "nuget: Ranvier.CSharp, 0.1.1-preview.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Ranvier.CSharp@0.1.1-preview.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Ranvier.CSharp&version=0.1.1-preview.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Ranvier.CSharp&version=0.1.1-preview.1&prerelease
                    
Install as a Cake Tool

Ranvier.CSharp

C# entry points for Ranvier: the Reactive factories take delegates, and extension methods cover Graph.Run, Signal.Update and the projection operators.

Per-node equality

Signals, memos (including owning and seeded forms), split effects and boundaries accept an optional typed comparer through additional overloads:

var name = Reactive.Signal("Ada", StringComparer.OrdinalIgnoreCase);
var label = Reactive.Memo(() => name.Value.Trim(), StringComparer.OrdinalIgnoreCase);
Reactive.EffectOn(() => label.Value, Console.WriteLine, StringComparer.OrdinalIgnoreCase);

Call these factories inside graph.Run or an active graph scope. Omit the comparer to use the graph policy. A supplied comparer applies only to that node; pending/error state transitions and ownership rules still apply. Null comparers throw ArgumentNullException.

Basic use

using Ranvier;
using Ranvier.CSharp;
using static Ranvier.CSharp.Reactive;

var graph = new Graph();

graph.Run(() =>
{
    var count = Signal(1);
    var doubled = Memo(() => count.Value * 2);
    Effect(() => Console.WriteLine($"doubled {doubled.Value}"));

    count.Value = 2;
});

Async values

A derived value over an async source, with loading and failure surfaced by a boundary instead of a hand-written busy flag and try/catch:

public sealed class SearchViewModel
{
    public SearchViewModel(ISearchService service)
    {
        Query = Signal("");
        Results = Async(token => service.SearchAsync(Query.Value, token));
        Summary = Boundary(
            () => Results.Value.Count == 0 ? "No matches" : string.Join(", ", Results.Value),
            () => "Searching…",
            error => $"Search failed: {error.Message}");
    }

    public Signal<string> Query { get; }
    public AsyncMemo<IReadOnlyList<string>> Results { get; }
    public Boundary<string> Summary { get; }
    public bool IsSearching => Summary.IsWaiting;
    public Exception? Error => Summary.Caught;
}

ISearchService declares one method, Task<IReadOnlyList<string>> SearchAsync(string query, CancellationToken token). Construct the view model inside graph.Run. The first read of Summary after a write to Query starts a new search and cancels the token of the one in progress; IsSearching and Error follow the boundary.

Binding to XAML

The core's fixed-dependency bindings copy one source to a target. The Reactive facade has no BindSignal or BindMemo factories; the XAML notification adapter below serves view-model properties.

ReactiveBindings raises INotifyPropertyChanged and INotifyDataErrorInfo for view-model properties backed by memos and signals, inside any existing view model; ReactiveObject is a base class over it. See the C# guide. ReactiveCommand is an ICommand whose CanExecute and busy state come from graph nodes; see Commands.

Editable collections

Create an editable source inside graph.Run, then derive views from its Rows:

var items = Reactive.KeyedCollection<(int Id, string Title), int>(item => item.Id);
items.Edit(edit =>
{
    edit.AddOrUpdate((1, "Write"));
    edit.AddOrUpdate((2, "Test"));
});
var titles = items.Rows.Select(item => item.Title);
using var reader = titles.NewValueReader();
reader.Read();
items.AddOrUpdate((2, "Retest"));
var delta = reader.Read();

An existing-key write preserves its position and updates its row directly. New keys append; Remove and Clear change membership. Edit batches synchronous writes and retains applied edits if the callback throws.

NewKeyReader reports membership and order; NewValueReader also reports KeyChange.Changed for unequal settled values under the graph policy. First reads and overflow report a reset. Deltas contain coalesced key hints: read current values through the projection and handle its pending/error states there. Dispose readers to release shared observation. See the C# collection guide.

Tracing

Tracing.Named and Tracing.Label compile in every build. The queries (Origin, Why, WhyDepth, WhyNot, History, WaitingOn, Snapshot, Resolve, Reconcile, Events, DumpText, Dump) return text, and exist only in the traced package, Ranvier.CSharp.Traced. It holds the same assembly, namespaces and version as Ranvier.CSharp, and depends on Ranvier.Traced. Switch between the two and define RANVIER_TRACE with them:

<PropertyGroup Condition="'$(RanvierTrace)' == 'true'">
    <DefineConstants>$(DefineConstants);RANVIER_TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
    <PackageReference Include="Ranvier.CSharp" Condition="'$(RanvierTrace)' != 'true'" />
    <PackageReference Include="Ranvier.CSharp.Traced" Condition="'$(RanvierTrace)' == 'true'" />
</ItemGroup>
var graph = new Graph();

graph.Run(() =>
{
    var count = Tracing.Named("count", () => Signal(1));
    var log = Effect(() => Console.WriteLine(count.Value));
    Tracing.Named("changes", () => EffectOn(() => count.Value, value => { }));
    Flush();

    count.Value = 2;
    Flush();
#if RANVIER_TRACE
    Console.WriteLine(Tracing.Why(graph, log));
    Console.WriteLine(Tracing.Why(graph, "/changes"));
#endif
});

Each per-node query takes the node, or its identity path. Tracing.Label is [Conditional("RANVIER_TRACE")]: the call stays only in a project that defines RANVIER_TRACE itself.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1-preview.1 74 10/28/2026
0.1.0-preview.7 56 10/26/2026
0.1.0-preview.6 57 10/21/2026
0.1.0-preview.5 69 10/4/2026
0.1.0-preview.4 70 10/3/2026
0.1.0-preview.3 64 9/30/2026
0.1.0-preview.2 66 9/30/2026
0.1.0-preview.1 59 9/29/2026

Preview release. The API may change before 1.0.