VoloGen.Equality.Generator
1.2.0
dotnet add package VoloGen.Equality.Generator --version 1.2.0
NuGet\Install-Package VoloGen.Equality.Generator -Version 1.2.0
<PackageReference Include="VoloGen.Equality.Generator" Version="1.2.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="VoloGen.Equality.Generator" Version="1.2.0" />
<PackageReference Include="VoloGen.Equality.Generator"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add VoloGen.Equality.Generator --version 1.2.0
#r "nuget: VoloGen.Equality.Generator, 1.2.0"
#:package VoloGen.Equality.Generator@1.2.0
#addin nuget:?package=VoloGen.Equality.Generator&version=1.2.0
#tool nuget:?package=VoloGen.Equality.Generator&version=1.2.0
VoloGen
What is VoloGen?
VoloGen is a collection of Roslyn incremental source generators that produce standard method overloads, interface implementations, and operators from core logic you write yourself.
VoloGen is not a value-object generator. It does not create types for you, impose wrapper structures, or dictate how your domain model looks. Instead, it takes the one method you already wrote — the single source of truth for parsing, formatting, comparing, or equality — and generates every remaining overload, interface member, and operator that the .NET type system expects.
The idea
Every .NET interface like IParsable<T>, IComparable<T>, or IEquatable<T> demands multiple method signatures that ultimately delegate to the same core logic. Writing them by hand is tedious, error-prone, and produces hundreds of lines of boilerplate that obscure the real business rules.
VoloGen flips this around:
- You define the core behavior — the "how" — in a single method.
- The generator produces all the standard overloads that delegate to your implementation.
- You stay in control — every generated method can be overridden by simply declaring it yourself.
┌──────────────────────────────────────────────────────────────────┐
│ Your code (the rule) │ Generated code (the overloads) │
│───────────────────────────────│─────────────────────────────────│
│ static bool TryParse(...) │ Parse(string, IFormatProvider?) │
│ │ Parse(ReadOnlySpan<char>, ...) │
│ │ TryParse(string?, ...) │
│ │ IParsable<T>.Parse(...) │
│ │ ISpanParsable<T>.Parse(...) │
│ │ IUtf8SpanParsable<T>.*(...) │
└──────────────────────────────────────────────────────────────────┘
Each generator ships as an independent NuGet package — use only what you need.
How it works
| You implement | Generator produces |
|---|---|
| One core method with your business logic | All standard overloads delegating to it |
| Nothing else | Interface implementations (IParsable<T>, IFormattable, etc.) |
Operators (==, !=, <, >, <=, >=) |
|
| Convenience overloads (string, span, UTF-8) |
The generator never invents behavior — every generated method is a thin wrapper that calls your code. If you already declared a specific overload, the generator skips it (skip-if-exists).
Features
- You define the rules — generators only produce overloads, never business logic.
- Zero runtime dependency — generated code is plain C#, no base classes or helper libraries.
- Incremental generation — only re-generates when the annotated type changes.
- Skip-if-exists — already have a custom
EqualsorParse? The generator won't overwrite it. - Cross-generator coordination —
[AutoComparable]and[AutoEquality]cooperate on==/!=. - Strong-name signed — all assemblies are signed with
VoloGen.snk. - Localized diagnostics — error messages are translated via
.resxresource files.
Packages
| Package | Attribute | What you write | What gets generated |
|---|---|---|---|
VoloGen.Parsable |
[AutoParsable] |
TryParse(ReadOnlySpan<char>, ...) |
Parse, TryParse overloads, IParsable<T>, ISpanParsable<T>, opt. IUtf8SpanParsable<T> |
VoloGen.Formattable |
[AutoFormattable] |
TryFormat(Span<char>, ...) |
ToString, TryFormat overloads, IFormattable, ISpanFormattable, opt. IUtf8SpanFormattable |
VoloGen.Comparable |
[AutoComparable] |
static int Compare(T, T) |
CompareTo overloads, IComparable<T>, IComparable, all comparison operators |
VoloGen.Equality |
[AutoEquality] |
static bool Equal(T, T) + GetHashCode() |
Equals overloads, IEquatable<T>, ==/!= operators |
Quick Start
1. Install
dotnet add package VoloGen.Parsable
dotnet add package VoloGen.Parsable.Generator
2. Write your core logic
using VoloGen;
[AutoParsable]
public partial struct Amount
{
private readonly decimal _value;
// This is the only method you write — the single source of truth.
// The generator will produce every other Parse/TryParse overload from it.
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out Amount result)
{
if (decimal.TryParse(s, provider, out var value))
{
result = new Amount { _value = value };
return true;
}
result = default;
return false;
}
}
3. Use the generated API
// All of these are generated — each one delegates to your TryParse above.
var a = Amount.Parse("42.5");
var b = Amount.Parse("42.5".AsSpan());
bool ok = Amount.TryParse("abc", out _); // false
bool ok2 = Amount.TryParse("100", CultureInfo.InvariantCulture, out _); // true
Core method → Generated overloads
AutoEquality
| You write | Generated |
|---|---|
static bool Equal(T left, T right) |
bool Equals(T) — IEquatable<T> |
override int GetHashCode() |
override bool Equals(object?) |
operator ==, operator != |
When
[AutoComparable]is also present,==/!=are emitted by the comparable generator to ensure consistency.
AutoComparable
| You write | Generated |
|---|---|
static int Compare(T left, T right) |
int CompareTo(T) — IComparable<T> |
int IComparable.CompareTo(object?) |
|
<, >, <=, >=, ==, != operators |
AutoParsable
| You write | Generated |
|---|---|
static bool TryParse(ReadOnlySpan<char>, IFormatProvider?, out T) |
Parse(string, IFormatProvider?) — throws ArgumentNullException if string arg is null (unless ThrowOnNull = false) |
Parse(ReadOnlySpan<char>, IFormatProvider?) |
|
TryParse(string?, IFormatProvider?, out T) — accepts null; converts via AsSpan() |
|
| Providerless & spanless convenience overloads | |
Optional flag: ImplementUtf8 = true |
IUtf8SpanParsable<T> overloads |
Optional flag: ImplementExact = true |
ParseExact / TryParseExact overloads |
Optional flag: ThrowOnNull = false |
Disables ArgumentNullException guard in Parse(string s, ...) overloads (default: true) |
AutoFormattable
| You write | Generated |
|---|---|
bool TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider?) |
IFormattable — ToString(string?, IFormatProvider?) |
Positive MaxBufferSize attribute property, const int MaxBufferSize, or ToString(string?, IFormatProvider?) |
ISpanFormattable convenience overloads |
override string ToString() |
|
Optional flag: ImplementUtf8 = true |
IUtf8SpanFormattable overloads |
Optional flag: DefaultFormat = "G" |
Default format for formatless overloads |
Optional flag: AllowNullFormatProvider = false |
Null-provider guard with ArgumentNullException |
Optional property: MaxBufferSize = 256 |
Buffer size for generated ToString; overrides the legacy constant |
Optional property: MaxStackBufferSize = 256 |
stackalloc threshold; defaults to 256, maximum 1024 |
Diagnostics
All diagnostics use the VoloGen category and are errors by default.
| ID | Name | Trigger |
|---|---|---|
| VG0001 | MissingTryParseMethod |
[AutoParsable] without core TryParse |
| VG0002 | MustBePartial |
Type is not partial |
| VG0003 | CannotBeStatic |
Type is static |
| VG0004 | CannotBeAbstract |
Type is abstract |
| VG0005 | MissingComparableField |
[AutoComparable] without Compare |
| VG0006 | MissingEquatableField |
[AutoEquality] without Equal / GetHashCode |
| VG0007 | MissingTryFormatMethod |
[AutoFormattable] without TryFormat |
| VG0008 | MissingToStringOrMaxBufferSize |
[AutoFormattable] without ToString or MaxBufferSize |
| VG0009 | InvalidMaxStackBufferSize |
Explicit MaxStackBufferSize is non-positive, greater than 1024, or greater than MaxBufferSize |
Project Structure
VoloGen/
+-- .github/ # CI workflows, issue templates, Copilot instructions
+-- samples/ # End-to-end usage examples
| +-- VoloGen.Samples.Formattable/
+-- src/
| +-- VoloGen.Common/ # Shared diagnostics, helpers, localized .resx
| +-- VoloGen.Equality/ # [AutoEquality] attribute
| +-- VoloGen.Equality.Generator/
| +-- VoloGen.Comparable/ # [AutoComparable] attribute
| +-- VoloGen.Comparable.Generator/
| +-- VoloGen.Parsable/ # [AutoParsable] attribute
| +-- VoloGen.Parsable.Generator/
| +-- VoloGen.Formattable/ # [AutoFormattable] attribute
| +-- VoloGen.Formattable.Generator/
+-- tests/
| +-- VoloGen.Equality.Tests/
| +-- VoloGen.Comparable.Tests/
| +-- VoloGen.Parsable.Tests/
| +-- VoloGen.Formattable.Tests/
| +-- VoloGen.Combinations.Tests/ # Multi-attribute combination tests
+-- docs/ # GitHub Pages documentation (Jekyll)
+-- build/ # Build scripts and documentation
+-- .editorconfig # Code style rules
+-- Directory.Build.props # Shared MSBuild properties
+-- VoloGen.snk # Strong-name signing key
+-- VoloGen.slnx # Solution file
Development
Prerequisites
- .NET 10 SDK or later
- Git
Build & Test
git clone https://github.com/OleksandrTsvirkun/VoloGen.git
cd VoloGen
dotnet tool restore
dotnet husky install
dotnet build
dotnet test
Run Samples
dotnet run --project samples/VoloGen.Samples.Formattable
The Formattable sample demonstrates attribute-based MaxBufferSize and
MaxStackBufferSize, the legacy const int MaxBufferSize fallback, generated
ToString overloads, and ISpanFormattable.TryFormat.
Releasing
See the Release Process documentation for how to tag and publish a new version.
Commit Policy
This project uses Conventional Commits enforced by Husky.NET + CommitLint.NET.
Format: <type>(<scope>): <description>
Types: feat, fix, refactor, docs, test, build, ci, chore, perf, style
Scopes: parsable, formattable, comparable, equality, common, ci, readme, examples
Contributing
Contributions are welcome! Please read the Contributing Guide before submitting a PR.
Code of Conduct
This project follows the Contributor Covenant Code of Conduct.
Security
To report a vulnerability, please see our Security Policy.
Acknowledgements
Inspired by the excellent work in Vogen by Steve Dunn and StronglyTypedId by Andrew Lock.
License
MIT -- Copyright (c) 2026 Oleksandr Tsvirkun
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.CSharp (>= 4.12.0)
- VoloGen.Equality (>= 1.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.