A .NET implementation of the Semantic Versioning 2.0.0 specification. Provides parsing, formatting, comparison, and JSON serialization of semantic versions as a lightweight, immutable readonly struct.
- Immutable value type —
readonly partial structwith value semantics - Full SemVer 2.0.0 compliance — major, minor, patch, pre-release, and build metadata
- Multiple parsing paths —
string,ReadOnlySpan<char>,ReadOnlySpan<byte>(UTF-8) - Multiple formatting paths —
ToString(),TryFormat(Span<char>),TryFormat(Span<byte>)(UTF-8) - Zero-allocation formatting — span-based
TryFormatoverloads allocate nothing - JSON serialization — built-in converters for both System.Text.Json and Newtonsoft.Json
- Comparison & equality — implements
IComparable<SemVer>,IEquatable<SemVer>, and all comparison/equality operators per the spec (build metadata is ignored in precedence) - Regex validation —
GeneratedRegex-based patterns for SemVer strings, pre-release identifiers, and build metadata identifiers - Interface-rich — implements
ISpanParsable<SemVer>,IUtf8SpanParsable<SemVer>,ISpanFormattable,IUtf8SpanFormattable,IFormattable
dotnet add package vm2.SemVerusing vm2;
// Parse
var version = SemVer.Parse("1.2.3-rc.1+build.7");
// Construct
var v = new SemVer(1, 2, 3, "rc.1", "build.7");
// Properties
Console.WriteLine(v.Major); // 1
Console.WriteLine(v.Minor); // 2
Console.WriteLine(v.Patch); // 3
Console.WriteLine(v.PreRelease); // "rc.1"
Console.WriteLine(v.BuildMetadata); // "build.7"
Console.WriteLine(v.IsPreRelease); // true
Console.WriteLine(v.IsStable); // false
Console.WriteLine(v.Core); // 1.2.3
// Format
Console.WriteLine(v.ToString()); // "1.2.3-rc.1+build.7"
// Compare (build metadata is ignored per spec)
var a = SemVer.Parse("1.0.0-alpha");
var b = SemVer.Parse("1.0.0-beta");
Console.WriteLine(a < b); // true
// Span-based formatting (zero allocation)
Span<char> buffer = stackalloc char[v.Length];
v.TryFormat(buffer, out int charsWritten);
// UTF-8 formatting (zero allocation)
Span<byte> utf8 = stackalloc byte[v.Length];
v.TryFormat(utf8, out int bytesWritten);Both converters are applied via attributes on the SemVer struct, so serialization works out of the box with no additional configuration.
The SemVerSysConverter uses span-based UTF-8 formatting and parsing for minimal allocations.
using System.Text.Json;
var v = new SemVer(1, 2, 3, "rc.1", "build.7");
string json = JsonSerializer.Serialize(v); // "\"1.2.3-rc.1+build.7\""
var parsed = JsonSerializer.Deserialize<SemVer>(json);The SemVerNsConverter provides serialization support for Newtonsoft.Json consumers.
using Newtonsoft.Json;
var v = new SemVer(1, 2, 3, "rc.1", "build.7");
string json = JsonConvert.SerializeObject(v); // "\"1.2.3-rc.1+build.7\""
var parsed = JsonConvert.DeserializeObject<SemVer>(json);-
Build:
dotnet restore dotnet build
-
Test:
-
from CLI, if it is not built yet (builds on MTP v2):
dotnet run --project test/SemVer.Tests/SemVer.Tests.csproj
-
from CLI, if it is already built in CLI or VSCode (MTP v2):
dotnet test --solution vm2.SemVer.slnx
-
-
Benchmarks:
dotnet run --project benchmarks/SemVer.Benchmarks/SemVer.Benchmarks.csproj -c Release -- --filter '*'[!TIP] In a personal development environment, you can run benchmarks with the
SHORT_RUNpreprocessor directive for faster (less accurate) iterations:`dotnet run --project benchmarks/SemVer.Benchmarks/SemVer.Benchmarks.csproj -c Release -p:preprocessor_symbols=SHORT_RUN -- --filter '*'`
- Package ID:
vm2.SemVer - License: MIT
- Repository: https://github.com/vmelamed/vm2.SemVer
src/SemVer/— library source codetest/SemVer.Tests/— xUnit v3 + MTP testsbenchmarks/SemVer.Benchmarks/— BenchmarkDotNet suite (Parse, Format, JSON)examples/SemVer.Example/— minimal console sampledocs/— documentationchangelog/— git-cliff configs for prerelease/release changelog updates
RE: regular expression.Charset: a set of characters (that is, a character-class fragment).
Charsets are strings that contain literal characters (for example, "abc") and/or character ranges (for example, "A-Z").
A charset may represent a BNF term that is defined as a set of characters, for example:
letter ::= "A" | "B" | "C" | ... | "Z" | "a" | "b" | "c" | ... | "z"
digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
Charsets can be concatenated, subject to .NET regex syntax rules, to build larger charsets, for example:
const string letterChars = "_A-Za-z";
const string digitChars = "0-9";
const string alphanumericChars = $"{letterChars}{digitChars}"; // => "_A-Za-z0-9" is a valid
// character class fragmentA charset is not a regular expression by itself; it is typically wrapped in square brackets to form one, for example:
const string letter = $"[{letterChars}]"; // real RE that matches a single letter characterBy convention, non-public charset constants use camelCase and the Chars suffix, for example letterChars.
Non-public constants in camelCase without a suffix represent regex fragments. Most of these are valid regex patterns on their own, but they are intended for composition rather than standalone use.
Whitespace rule:
If a fragment includes readability spaces around operators (for example, around |), every Regex instance that includes that
fragment must be compiled or generated with RegexOptions.IgnorePatternWhitespace.
Rex vs Regex:
*Rexconstants are generally unanchored patterns intended for composition or searching within larger strings.*Regexconstants are full-string validation patterns, typically anchored with^and$.
Only *Regex constants get public Regex instance producing factory methods.
These methods are named after the constant without the Regex suffix (for example, SemVer20()), and are generated via
GeneratedRegexAttribute using the corresponding *Regex pattern.
Quick convention table:
| Visibility | Suffix | Naming convention | Description |
|---|---|---|---|
| Non-public | *Chars | camelCase | character-class fragments (not standalone regex patterns) |
| Non-public | camelCase | regex fragments for composition | |
| Public | *Rex | PascalCase | generally unanchored public patterns |
| Public | *Regex | PascalCase | anchored full-string validation patterns |
| Public | *Regex | PascalCase | GeneratedRegex factories for *Regex constants (method name is the constant name without the Regex suffix) |
Note
This style of building the regexes usually requires that the regular expression objects MUST be built with the
options RegexOptions.IgnorePatternWhitespace and RegexOptions.ExplicitCapture for correctness, better readability, and
performance.