Skip to main content
v3.0.1Open source · MIT · .NET 8, 9 and 10

Collections that are never empty.

NonEmptyList<T> is a list the type system guarantees has at least one element. Head, Max, Average and Reduce cannot fail for lack of data, LINQ keeps the guarantee whenever it can, and a bundled analyzer catches empty input at build time.

dotnet add package Masterly.NonEmptyList
Numbers.cs
NonEmptyList<int> numbers = [3, 1, 2];

var doubled = numbers.Select(n => n * 2);
var sorted = numbers.OrderBy(n => n);
var large = numbers.Where(n => n > 10);
var max = numbers.Max();

NonEmptyList<int> none = [];
Inferred typesBuilding…
  1. CHECKINGdoubled
  2. PENDINGsorted
  3. PENDINGlarge
  4. PENDINGmax
  5. PENDINGNEL001
0
dependencies in the core package
8
packages, one shared version
Built in
Roslyn analyzer, code fixes and source generator
.NET 8+
targets .NET 8, .NET 9 and .NET 10
Write

One guarantee, in every kind of C# you write

Collection expressions, list patterns, query syntax and generic math all know the list is non-empty, so the code you already write stops needing defensive checks.

Create lists with [..], match them with list patterns and slice them with ranges. Head never throws. Learn more

NonEmptyList<int> numbers = [3, 1, 2];
var names = NonEmptyList.Of("Ada", "Grace", "Linus");

int first = numbers.Head;
int last = numbers.Last;

if (numbers is [var head, .. var rest])
{
Console.WriteLine($"{head} then {rest.Count} more");
}

List<int> tail = numbers[1..];
Capabilities

Everything a non-empty collection needs in production

Start by replacing one List<T>. Add constraints, validation and framework integration when you need them.

Type-preserving LINQ

Select, OrderBy, GroupBy, Distinct, Concat and Chunk keep the guarantee. Where, Skip and Take return IEnumerable, so the type never lies.

Read the guide
Return type of each LINQ operator on a NonEmptyList
On a NonEmptyList<T>Returns
Select(f)NonEmptyList<R>
OrderBy(k)OrderedNonEmptyList<T>
GroupBy(k)NonEmptyList<NonEmptyGrouping<K, T>>
Max()T
Where(p)IEnumerable<T>may be empty

A guarantee you cannot break

The list wraps its storage. Emptying it through IList<T>, ICollection<T> or IList throws instead of succeeding.

Read the guide

Generic math

Sum, Product, Average, Median and Mode for any System.Numerics type, including your own.

Read the guide

Errors that accumulate

Validation<T, TError> combines up to eight checks and reports every failure at once.

Read the guide

Sets, dictionaries, strings

NonEmptySet, NonEmptyDictionary, NonEmptyString, immutable lists, spans and lazy sequences.

Read the guide

Constraints in the type

AtLeast, AtMost, Exactly, Distinct and Sorted, composed with And and Or, checked once at construction.

Read the guide
Members each constraint makes available
ConstrainedList<T, …>You get
NonEmptyHead, Last, Max()
AtLeast<Two>+ Second()
AtLeast<Three>+ Third()
And<AtLeast<Two>, Distinct>Second(), no duplicates

Modeling types

A zipper that always has a focus, undo history, event streams with an origin, and rose trees.

Read the guide

Non-empty batching

Batch streams and channels into NonEmptyList batches, by size or by time. No more empty-batch checks.

Read the guide

Analyzer and generator

Catch [] and provably empty input at build time, and generate domain collections from one attribute.

Read the guide

Works with your stack

Query-string binding, OpenAPI schemas, options validation, EF Core, Json.NET, MessagePack and Dapper.

Read the guide
Packages

Install the core. Add only what you use.

All eight packages ship together with the same version number. Every companion package depends only on the core and the library it integrates with.

Replace your first List<T> in five minutes.

The Quick Start creates a list, reads it safely, queries it and handles empty input.