Open-source project
Cysharp/ZLinq avatar
Cysharp/ZLinq

ZLinq: zero-allocation LINQ for .NET, Unity and Godot

Zero allocation LINQ with LINQ to Span, LINQ to SIMD, and LINQ to Tree (FileSystem, JSON, GameObject, etc.) for all .NET platforms and Unity, Godot.

5,282 stars216 forksC#MIT

At a glance

What is it?
ZLinq keeps LINQ's method-chaining syntax but moves the enumerator into a struct, so chains stop allocating. It targets .NET, Unity and Godot, and adds LINQ over Span, SIMD and tree structures.
Who is it for?
Adopt ZLinq if you write C# where per-frame or per-request LINQ allocations show up in profiles, and you want to keep the method-chain syntax. Do not adopt it if your code passes sequences across method boundaries as IEnumerable<T>, or if you rely on LINQ behaviour the README lists as a difference or limitation.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 84 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What ZLinq fixes, and for whom

Standard LINQ allocates. Each operator you chain adds another iterator object on the heap, and in a game loop or a hot request path those objects become garbage that the collector has to clean up later. ZLinq's README states the goal directly: unlike regular LINQ, ZLinq does not increase allocations as you add more method chains, and it also has higher basic performance. The project is published by Cysharp and licensed under MIT.

The audience is C# developers who already like LINQ's shape and do not want to give it up. That includes Unity and Godot developers, since the package targets all .NET platforms (netstandard2.0, 2.1, net8, net9) plus Unity and Godot. The README frames the performance work as suitable for demanding applications like games. If your application is a short-lived console tool, the allocation saving is real but probably not the reason you would switch.

One secondary benefit is worth naming because it is unusual. The README says operators and optimizations equivalent to .NET 10 can be used on .NET Framework 4.8 (netstandard2.0) and Unity (netstandard2.1). So a project pinned to an older runtime can still get newer LINQ operators, including Shuffle, RightJoin, LeftJoin, Sequence and InfiniteSequence.

How ValueEnumerable moves the enumerator into a struct

The core type is a readonly ref struct that wraps a struct enumerator:

csharp
public readonly ref struct ValueEnumerable<TEnumerator, T>(TEnumerator enumerator)
    where TEnumerator : struct, IValueEnumerator<T>, allows ref struct
{
    public readonly TEnumerator Enumerator = enumerator;
}

Because the enumerator is a struct carried inside the sequence value, chaining operators produces new struct values rather than new heap objects. That is the mechanism behind the zero-allocation claim.

The enumerator interface merges MoveNext and Current into one call, TryGetNext, to cut iterator calls, and it exposes three optimization hooks: TryGetNonEnumeratedCount, TryGetSpan and TryCopyTo. These let an operator skip work when the underlying data is already countable, already a span, or copyable in bulk. The README gives ToArray as an example that benefits from TryGetNonEnumeratedCount, and notes that First, Last and ElementAt can be served through TryCopyTo by passing a single-element Span with an Index.

The design also solves a problem that sank earlier struct-based LINQ attempts. C# cannot infer types from generic constraints, so the traditional approach needed every operator combination written as an instance method, which the README describes as resulting in 100,000+ methods and massive assembly sizes. ZLinq's README states it avoids that boilerplate by conveying types to the compiler another way. The README does not spell out that mechanism in the excerpt available; it points to a linked blog post for the architecture details.

Installing ZLinq and writing a first query

The package is on NuGet and the README gives a single install command.

bash
dotnet add package ZLinq

After that, add the using directive and call AsValueEnumerable on the source. The README calls this the only line you need to add:

csharp
using ZLinq;

var seq = source
    .AsValueEnumerable() // only add this line
    .Where(x => x % 2 == 0)
    .Select(x => x * 3);

foreach (var item in seq) { }

What you should see is the same sequence your existing LINQ chain would produce, iterated with a foreach. The difference is in the allocation profile, not the values.

The README's getting-started example also shows the array case explicitly, and then a Span case that only compiles in a .NET 9 and C# 13 environment that supports allows ref struct:

csharp
using ZLinq;

var source = new int[] { 1, 2, 3, 4, 5 };
var seq1 = source.AsValueEnumerable().Where(x => x % 2 == 0);

Span<int> span = stackalloc int[5] { 1, 2, 3, 4, 5 };
var seq2 = span.AsValueEnumerable().Select(x => x * x);

For Unity and Godot, the README defers to its own Unity and Godot sections rather than restating the steps here. There is also an optional drop-in replacement source generator that the README describes as automatically accelerating all LINQ methods, which is a different adoption path from adding AsValueEnumerable by hand.

LINQ to Span, LINQ to SIMD and LINQ to Tree

Three extensions go beyond replacing standard LINQ.

LINQ to Span gives full LINQ operations on Span<T>, which the README attributes to .NET 9 and C# 13's allows ref struct support. This is the piece that lets the same chain style apply to stack-allocated or sliced memory without copying it into an array first. The constraint is real: on older target frameworks this path is not available, so code written against it will not compile there.

LINQ to SIMD applies SIMD automatically where possible, and the README says operations can also be customized. The README does not give a list of which operators get vectorized, so treat automatic application as something to verify against your own workload rather than assume.

LINQ to Tree extends tree-structured objects, with built-in support named for FileSystem, JSON and GameObject. This is the most opinionated part of the library. Instead of flattening a directory or a scene graph into a list first, you query the structure. For Unity users, GameObject support means scene traversal in LINQ form; for tooling, FileSystem and JSON cover the common cases. The README does not document how to plug in your own tree type in the excerpt available.

Where ZLinq is the wrong choice

The README points readers to a Difference and Limitation section, which is a signal in itself: this is not a silent drop-in for every codebase. The type you get back is a ValueEnumerable, not an IEnumerable<T>. That matters at API boundaries. If your method signature returns IEnumerable<T> and a caller consumes it later, the struct-based sequence does not travel through that interface without boxing or conversion, and the allocation advantage is gone.

Because the sequence is a ref struct, it is subject to ref struct rules. It cannot be stored in a field of a class, captured in certain ways, or used across an await boundary in the way an ordinary object can. The README does not enumerate these restrictions in the excerpt, so a team considering adoption should read that section before rewriting call sites.

The compatibility claim is strong but bounded. The README states 99% compatibility with .NET 10's LINQ and says the project runs dotnet/runtime's System.Linq.Tests as a drop-in replacement, passing 9000 tests. That is a meaningful signal about coverage. It is also explicitly not 100%, and the README does not list which cases fall outside. If your code depends on a corner of LINQ semantics, that corner is exactly what you need to check.

Finally, if your allocation profile is dominated by something else, such as string formatting or serialization, switching LINQ implementations will not move the number you care about.

ZLinq against SimdLinq and hand-written loops

The nearest alternative from the same author is SimdLinq, which the README lists among the prior work that informed ZLinq. The difference in approach is scope. SimdLinq is about vectorizing aggregate operations over arrays; ZLinq is a full LINQ surface with a struct enumerator, and SIMD is one of its extensions rather than its whole purpose. If all you need is a faster Sum or Average over a numeric array, the narrower library is the smaller dependency. If you want to keep Where, Select, GroupBy and the rest in chain form, ZLinq is the one that covers that ground.

The other alternative is writing the loops by hand. That gives you complete control and zero framework overhead, and for a single hot path it is often the right answer. The cost is that you lose the declarative shape, and every new query is new code to maintain. ZLinq's pitch is that you keep the shape and drop the allocation, which is a different trade from either abandoning LINQ or accepting its cost.

A third option is the source generator the README describes as an optional drop-in replacement, which accelerates existing LINQ methods without rewriting them as chains. The README does not document its configuration in the excerpt, so the practical difference between the generator path and the AsValueEnumerable path is something to confirm in the repository.

Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-07-08. Releases are tagged and spaced: 1.5.4 on 2025-11-13, 1.5.5 on 2026-02-12 and 1.5.6 on 2026-04-08. That is a steady cadence rather than a burst, and the version numbers stay within the 1.5 line, which suggests incremental change rather than churn.

Upgrade cost is shaped by the compatibility promise. Because ZLinq tracks .NET 10's LINQ surface, moving to a newer ZLinq should mostly mean picking up operators you did not have. The risk sits in the same place as adoption: semantics that differ from System.Linq. The README's Difference and Limitation section is the document to re-read at each upgrade, and the test suite it runs is the evidence to weigh.

The licence is MIT. That is permissive and short, and it is the same licence family used across many Cysharp packages. This is not legal advice; if your organisation has licence review, MIT is normally straightforward but the review is still yours to run. Note that the repository contains an opensource.snk file, which is a strong-naming key, and the README does not discuss what that means for signed assemblies you produce.

Editorial conclusion

Adopt ZLinq if you write C# where per-frame or per-request LINQ allocations show up in profiles, and you want to keep the method-chain syntax. Do not adopt it if your code passes sequences across method boundaries as IEnumerable<T>, or if you rely on LINQ behaviour the README lists as a difference or limitation. Before committing, check the Difference and Limitation section against your own call sites, and confirm that your target framework matches the netstandard2.0, netstandard2.1, net8 or net9 builds the package ships, since LINQ to Span needs .NET 9 and C# 13.

Frequently asked questions

What does ZLinq do?

It is a zero-allocation LINQ implementation for C#. You call AsValueEnumerable on a source and keep chaining Where, Select and the rest, but the enumerator is a struct rather than a heap object, so chains do not add allocations.

How do I install ZLinq in a C# project?

The README gives one command, dotnet add package ZLinq, which pulls the package from NuGet. After that you add using ZLinq and call AsValueEnumerable on the sequence you want to query.

Does ZLinq work with Unity and Godot?

Yes. The README states the library targets all .NET platforms plus Unity and Godot, and it links to dedicated Unity and Godot sections for the setup steps rather than repeating them in the main text.

Is ZLinq a drop-in replacement for standard LINQ?

The README claims 99% compatibility with .NET 10's LINQ and says the project passes 9000 tests from dotnet/runtime's System.Linq.Tests as a drop-in replacement. It also points readers to a Difference and Limitation section, so it is not a silent swap for every call site.

Can I use LINQ to Span on any .NET version?

No. The README ties full LINQ operations on Span<T> to .NET 9 and C# 13, because it depends on allows ref struct. On earlier target frameworks that path will not compile.

Official sources

  1. Cysharp/ZLinq on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cysharp-zlinq.svg)](https://hysenlabs.com/projects/cysharp-zlinq)