Skip to content

About

A zero-allocation async INPC library for .NET. Roslyn source generator wires PropertyChanged/PropertyChanging dispatch at compile time — no reflection, fully awaitable.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

320 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ZeroAlloc.Notify

NuGet Build codecov License: MIT AOT GitHub Sponsors

Source-generated, awaitable INotifyPropertyChanged for .NET: await vm.SetNameAsync("Alice") returns only after every async handler has finished, with no reflection and NativeAOT support.

Why ZeroAlloc.Notify

  • Setters you can await. Async validation, saving or coordinated state changes complete before the setter returns. CommunityToolkit.Mvvm and PropertyChanged.Fody only offer synchronous notifications.
  • 0 B when nobody listens. The generated setter creates event args only when there are subscribers; tests assert 0 B for reference-type and value-type properties. With subscribers, a change costs its args object, 40 B, plus a box per value-type value; tests pin both figures.
  • Works under NativeAOT and trimming. All plumbing is generated at compile time; CI publishes and runs a NativeAOT sample on every build.
  • Compile-time diagnostics. A class that is not partial, a record, or a stray [ObservableProperty] is reported by the build, not discovered at runtime.

Install

dotnet add package ZeroAlloc.Notify

The source generator ships inside the package.

Quick start

using ZeroAlloc.Notify;

var user = new UserViewModel();

user.PropertyChangedAsync += async (args, ct) =>
{
    await Task.Delay(100, ct);  // e.g. save to a server
    Console.WriteLine($"{args.PropertyName}: {args.OldValue} -> {args.NewValue}, saved");
};

await user.SetNameAsync("Alice");  // returns after the handler has saved
await user.SetAgeAsync(30);

[NotifyPropertyChangedAsync]
public partial class UserViewModel
{
    [ObservableProperty] private string _name = "";
    [ObservableProperty] private int _age;
}

The generator writes the Name and Age properties, the SetNameAsync and SetAgeAsync setters and the PropertyChangedAsync event.

What you get

  • Async property notifications, before and after a change: [NotifyPropertyChangedAsync] and [NotifyPropertyChangingAsync]. Observable properties
  • Async collection notifications with [NotifyCollectionChangedAsync]. Collection changes
  • Async validation through INotifyDataErrorInfoAsync. Validation
  • Parallel handlers by default, sequential on request with [InvokeSequentially], and a CancellationToken on every setter. Async notifications
  • Build-time checks, ZAN001 to ZAN006. Diagnostics
  • Targets netstandard2.0, net8.0 and net10.0.
  • Built on ZeroAlloc.AsyncEvents, the async event primitive behind every handler.

Performance

One property change with five handlers attached, .NET 10, all four libraries in one BenchmarkDotNet run. Allocations are exact; the ratio is each library's time against a hand-written INotifyPropertyChanged in the same run. Absolute timings are not published.

Library Ratio Allocated Async handlers
Manual INotifyPropertyChanged 1.00 24 B no
PropertyChanged.Fody 0.93 0 B no
CommunityToolkit.Mvvm 1.81 0 B no
ZeroAlloc.Notify 1.47 40 B yes

Awaitable handlers have a cost: Notify is slower than hand-written code and Fody, and allocates its args object when someone listens. For purely synchronous view models, Fody is the faster choice. The .NET 8 ratios are in the performance doc. Allocation breakdown, design notes and full numbers: docs/performance.md.

Learn more

About

A zero-allocation async INPC library for .NET. Roslyn source generator wires PropertyChanged/PropertyChanging dispatch at compile time — no reflection, fully awaitable.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages