Skip to content

Semantic Versions

Frank Stüber edited this page Aug 25, 2026 · 2 revisions

The following sections provide a more detailed look at the SemanticVersion class.

Creating Versions

Create a semantic version directly using the constructor:

using Enbrea.SemVer;

var version = new SemanticVersion(1, 2, 3);

With a prerelease label:

var version = new SemanticVersion(1, 2, 3, "beta");

With prerelease information and build metadata:

var version = new SemanticVersion(1, 2, 3, "rc.1", "build.42");

Build metadata can also be supplied without prerelease information:

var version = new SemanticVersion(1, 2, 3, buildMetadata: "build.42");

Major, minor, and patch numbers must be non-negative. Prerelease and build metadata values are validated according to the Semantic Versioning 2.0.0 rules.

Parsing Version Strings

Use SemanticVersion.Parse() to parse Semantic Versioning 2.0.0 strings:

var release = SemanticVersion.Parse("1.2.3");
var prerelease = SemanticVersion.Parse("1.2.3-beta");
var build = SemanticVersion.Parse("1.2.3+build.42");
var complete = SemanticVersion.Parse("1.2.3-rc.1+build.42");

Parse is also available for ReadOnlySpan<char>:

ReadOnlySpan<char> value = "1.2.3-beta.1";

var version = SemanticVersion.Parse(value);

When parsing input that may be invalid, use TryParse() to avoid exceptions:

if (SemanticVersion.TryParse("1.2.3", out var version))
{
    Console.WriteLine(version);
}

TryParse also supports ReadOnlySpan<char>:

ReadOnlySpan<char> value = "1.2.3-beta.1";

if (SemanticVersion.TryParse(value, out var version))
{
    Console.WriteLine(version);
}

Important

Parsing is strict and follows the Semantic Versioning 2.0.0 specification. Versions such as 1, 1.2, 01.2.3, or 1.2.3-alpha.01 are rejected.

Validating Version Strings

Use SemanticVersion.IsValid() when you only need to determine whether a value represents a valid semantic version:

Console.WriteLine(SemanticVersion.IsValid("1.2.3"));                  // True
Console.WriteLine(SemanticVersion.IsValid("1.2.3-alpha.1+build.42")); // True
Console.WriteLine(SemanticVersion.IsValid("1.2"));                    // False
Console.WriteLine(SemanticVersion.IsValid("01.2.3"));                 // False

A ReadOnlySpan<char> overload is also available:

ReadOnlySpan<char> value = "2.0.0";

Console.WriteLine(SemanticVersion.IsValid(value)); // True

Version Components

A semantic version consists of the following components:

Property Description
Major Major version number
Minor Minor version number
Patch Patch version number
PreRelease Optional prerelease identifiers
BuildMetadata Optional build metadata
IsPrerelease Indicates whether prerelease information is present
HasBuildMetadata Indicates whether build metadata is present

Example:

var version = SemanticVersion.Parse("1.2.3-alpha.1+build.42");

Console.WriteLine(version.Major);            // 1
Console.WriteLine(version.Minor);            // 2
Console.WriteLine(version.Patch);            // 3
Console.WriteLine(version.PreRelease);       // alpha.1
Console.WriteLine(version.BuildMetadata);    // build.42
Console.WriteLine(version.IsPrerelease);     // True
Console.WriteLine(version.HasBuildMetadata); // True

Modifying Versions

SemanticVersion is immutable. Use the With... methods to create a version with an updated component:

var version = SemanticVersion.Parse("1.2.3-alpha+build.9");

var newMajor = version.WithMajor(2);
var newMinor = version.WithMinor(5);
var newPatch = version.WithPatch(7);

Console.WriteLine(newMajor); // 2.2.3-alpha+build.9
Console.WriteLine(newMinor); // 1.5.3-alpha+build.9
Console.WriteLine(newPatch); // 1.2.7-alpha+build.9

Prerelease information and build metadata can also be changed:

var version = SemanticVersion.Parse("1.2.3");

var prerelease = version.WithPreRelease("beta.1");
var withBuild = prerelease.WithBuildMetadata("build.42");

Console.WriteLine(prerelease); // 1.2.3-beta.1
Console.WriteLine(withBuild);  // 1.2.3-beta.1+build.42

Use WithoutPreRelease() and WithoutBuildMetadata() to remove optional components:

var version = SemanticVersion.Parse("1.2.3-rc.1+build.42");

Console.WriteLine(version.WithoutPreRelease());
// 1.2.3+build.42

Console.WriteLine(version.WithoutBuildMetadata());
// 1.2.3-rc.1

Formatting

Convert a semantic version back to its canonical string representation using ToString():

var version = new SemanticVersion(1, 2, 3, "rc.1", "build.42");

Console.WriteLine(version);
// 1.2.3-rc.1+build.42

Comparing Versions

SemanticVersion implements IComparable<SemanticVersion>, IEquatable<SemanticVersion>, IParsable<SemanticVersion>, and ISpanParsable<SemanticVersion>.

It also supports the standard comparison and equality operators.

There is an important distinction between precedence and equality:

  • CompareTo, <, <=, >, and >= compare SemVer precedence and ignore build metadata.
  • Equals, ==, and != compare all version components, including build metadata.
  • HasSamePrecedence() explicitly tests whether two versions have the same SemVer precedence.

Equality

Two versions are equal when all their components are equal:

var v1 = SemanticVersion.Parse("1.2.3");
var v2 = SemanticVersion.Parse("1.2.3");

Console.WriteLine(v1 == v2);
// True

Build metadata is part of equality:

var v1 = SemanticVersion.Parse("1.2.3+build.1");
var v2 = SemanticVersion.Parse("1.2.3+build.2");

Console.WriteLine(v1 == v2);
// False

Precedence

Ordering follows the Semantic Versioning precedence rules:

var v1 = SemanticVersion.Parse("1.2.3");
var v2 = SemanticVersion.Parse("2.0.0");

Console.WriteLine(v1 < v2);   // True
Console.WriteLine(v1 <= v2);  // True
Console.WriteLine(v2 > v1);   // True
Console.WriteLine(v2 >= v1);  // True

Use HasSamePrecedence() when build metadata should be ignored:

var v1 = SemanticVersion.Parse("1.0.0+build.1");
var v2 = SemanticVersion.Parse("1.0.0+build.2");

Console.WriteLine(v1.HasSamePrecedence(v2));
// True

The same result can be obtained through CompareTo():

Console.WriteLine(v1.CompareTo(v2));
// 0

Prerelease Precedence

Prerelease versions always have lower precedence than the corresponding release version:

var prerelease = SemanticVersion.Parse("1.0.0-rc.1");
var release = SemanticVersion.Parse("1.0.0");

Console.WriteLine(prerelease < release);
// True

The implementation follows the precedence rules defined by the Semantic Versioning specification:

1.0.0-alpha
1.0.0-alpha.1
1.0.0-alpha.beta
1.0.0-beta
1.0.0-beta.2
1.0.0-beta.11
1.0.0-rc.1
1.0.0

Numeric prerelease identifiers are compared numerically, even when they exceed the range of a 32-bit integer.

Numeric identifiers always have lower precedence than non-numeric identifiers.

Build Metadata

Build metadata is specified after a plus sign (+):

1.0.0+build.1
1.0.0-alpha+build.42
1.0.0-rc.1+20250810

Build metadata is preserved during parsing and formatting:

var version = SemanticVersion.Parse("1.0.0+build.42");

Console.WriteLine(version.BuildMetadata);
// build.42

Console.WriteLine(version.HasBuildMetadata);
// True

Precedence and Equality

According to the Semantic Versioning specification, build metadata does not affect version precedence:

var v1 = SemanticVersion.Parse("1.0.0+build.1");
var v2 = SemanticVersion.Parse("1.0.0+build.2");

Console.WriteLine(v1.CompareTo(v2));         // 0
Console.WriteLine(v1.HasSamePrecedence(v2)); // True
Console.WriteLine(v1 <= v2);                 // True
Console.WriteLine(v1 >= v2);                 // True

However, SemanticVersion preserves build metadata as part of its value, so versions with different build metadata are not equal:

Console.WriteLine(v1 == v2); // False
Console.WriteLine(v1 != v2); // True