Open-source project
angularsen/UnitsNet avatar
angularsen/UnitsNet

UnitsNet: Strongly Typed Units of Measurement for C#

Makes life working with units of measurement just a little bit better.

2,971 stars415 forksC#MIT-0

At a glance

What is it?
UnitsNet is an MIT-0 licensed C# library that replaces magic numbers and guessed units with typed quantities, operator overloads and culture-aware parsing. It is on NuGet, targets .NET Standard 2.0 through .NET 10 preview, and the master branch is now a v6 pre-release.
Who is it for?
Adopt UnitsNet if your C# codebase passes raw doubles between layers and you want compile-time separation between mass, force and length. Do not adopt it if you need a unit library outside .NET, or if you cannot tolerate a pre-release branch, because master now targets v6 while new units are backported to maintenance/v5.
Can I use it commercially?
Yes. MIT-0 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 59 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

The problem UnitsNet solves for C# teams

Passing a double between methods tells the next reader nothing about what it means. A weight in kilograms and a force in newtons are both doubles, and the compiler will happily let you add them. UnitsNet makes each physical quantity its own type: Length, Mass, Force, Speed, Acceleration, RotationalSpeed and many more. The README states the goal plainly: "No more magic constants found online or guessing the unit of variables."

The audience is .NET developers who deal with measurements at boundaries: parsing user input, reading sensor or CSV data, converting for display, or serializing quantities over an API. The README example makes the type-safety argument concrete. Inside a method that takes a Mass, writing weight.Newtons is a compile error, because newtons belong to Force, not Mass. That is the whole pitch: the mistake is caught before the code runs.

It is not a calculator app or a CLI. It is a library you reference from your own code, and the repository ships sample projects under Samples/, including UnitConverter.Console and UnitConverter.Wpf, for people who want a runnable starting point.

How UnitsNet represents quantities and converts them

Each quantity is a struct-like type backed by a numeric value and an enum identifying the unit. The README shows two construction paths: Length.FromMeters(1) and new Length(2, LengthUnit.Meter). Conversion is done through properties, so meter.Centimeters returns 100 and meter.Inches returns 39.3701. The unit definitions themselves live in JSON files, which is why the README refers to Length.json when explaining that a unit can have more than one abbreviation.

Operator overloads are where the design gets interesting. The README shows arithmetic within a quantity, such as 2 * Length.FromMeters(1), and also composition across quantities: Speed.FromKilometersPerHour(80) * TimeSpan.FromMinutes(30) produces a Length, while Force.FromNewtons(100) / Mass.FromKilograms(20) produces an Acceleration. Those operations encode dimensional relationships in the type system rather than in comments.

Culture handling is a documented part of the surface. Abbreviations default to Thread.CurrentCulture and fall back to US English when a culture has no definition. ToString(), GetAbbreviation(), Parse/TryParse() and ParseUnit/TryParseUnit() all consult culture. The README shows one kilogram rendering as "1 кг" under ru-RU, and a custom format string producing "unit: mg, value: 1.00" under en-US versus "unit: мг, value: 1,00" under ru-RU. If you build a UI that shows units to end users, that behavior is the difference between a library and a weekend of string work.

Installing UnitsNet via NuGet and converting your first value

The README gives one install command. Run it in the project directory that needs the library, and NuGet resolves the package from nuget.org.

bash
dotnet add package UnitsNet

After that, construct a quantity and read it back in another unit. The README's static typing example is the shortest path to seeing whether the model fits your code.

C#
using UnitsNet;

Length meter = Length.FromMeters(1);
Length twoMeters = new Length(2, LengthUnit.Meter);

double cm = meter.Centimeters;   // 100
double yards = meter.Yards;      // 1.09361
double feet = meter.Feet;        // 3.28084
double inches = meter.Inches;    // 39.3701

If you want the shorter construction syntax, the README documents two extension packages with different requirements. For C# 13 and lower, install UnitsNet.NumberExtensions and call the method with parentheses.

bash
dotnet add package UnitsNet.NumberExtensions
C#
using UnitsNet.NumberExtensions.NumberToLength;

Length distance = 5.Meters();  // Instead of Length.FromMeters(5)

For C# 14 projects, the README points at UnitsNet.NumberExtensions.CS14, which uses extension members syntax without parentheses, so the same idea reads as 5.Meters. The README notes that this variant currently requires <LangVersion>preview</LangVersion> and a .NET 10 SDK preview. Pick one of the two extension packages, not both, and match it to your language version.

AmbiguousUnitParseException and other limits worth knowing

Parsing is the sharp edge. Some units of the same quantity share an abbreviation, so Length.Parse("1 pt") throws AmbiguousUnitParseException with the message Cannot parse "pt". The README is direct about the consequence: there is no built-in way to avoid this. Your options are to guarantee that input never contains an ambiguous abbreviation, or to write a parser that knows which units your application prefers. If you accept free-form unit strings from users, budget for that work.

Parsing is also culture-bound. Mass.Parse("1.0 kg", usEnglish) passes a culture explicitly, which is the safe habit; relying on the ambient thread culture means the same string can parse differently depending on where the code runs.

There is a versioning constraint on top of the API constraints. The README states that master now targets v6 and is still in pre-release, and that new units will be backported to maintenance/v5 until v6 becomes stable. That is a real decision point: tracking master gets you the new work, but the branch is described as pre-release, and the recent release list includes UnitsNet/6.0.0-pre020 alongside the stable UnitsNet/5.75.1. If your project cannot take pre-release dependencies, stay on the 5.x line and follow the wiki upgrade notes rather than the branch.

Serialization, modular builds and the .NET-only boundary

The repository layout shows dedicated serialization packages: UnitsNet.Serialization.JsonNet and UnitsNet.Serialization.SystemTextJson, each with its own test project. The README's table of contents lists a "Serialize to JSON, XML and more" section, so serialization is a first-class concern rather than something you assemble yourself from value and unit strings. If you send quantities over an API, check which of the two serializers matches the JSON stack you already use.

There is also an experimental path. The README flags UnitsNet.Modular as experimental, describing it as generating only the quantities and units your application needs. That matters if the full set of generated types is more than you want in your build. It is labeled experimental in the README, so treat it as something to evaluate rather than the default install.

The boundary is .NET. The build targets listed are .NET Standard 2.0, .NET 8.0 (LTS), .NET 9.0 and .NET 10.0 (preview). The topics include powershell, and the README has a "Units.NET on other platforms" section, but the package itself is a .NET library. A JavaScript or Python service in the same system cannot share these types; it can only exchange numbers or strings across a boundary you define. The related searches include "unitsnet js", and the honest answer is that the library is C#.

UnitsNet compared with hand-rolled conversion helpers

The realistic alternative is not another library; it is the conversion helper class most teams already have. A static class with const double MetersPerFoot and a few multiply methods. That approach has one advantage: nothing to install, nothing to upgrade, and no generated code in your build. It also has no type separation at all. Every value is a double, so a method that takes a length in meters will accept a mass in kilograms without complaint.

The difference in approach shows up in two places. First, composition: UnitsNet expresses Speed multiplied by TimeSpan as a Length, and Force divided by Mass as an Acceleration, through operator overloads. A helper class has no equivalent unless you write and maintain those combinations yourself. Second, parsing and culture: UnitsNet ships Parse, TryParse, ParseUnit and GetAbbreviation with culture-aware abbreviations for dozens of quantities. A hand-rolled helper typically handles one locale and a handful of units.

The trade-off runs the other way too. A helper class has no AmbiguousUnitParseException, because it never guesses. It has no pre-release branch to track. And it adds nothing to your binary. If your application converts exactly one unit pair in one place, UnitsNet is more machinery than the problem needs.

Maintenance, licensing and what to check before upgrading

The repository is not archived, and the last push was on 2026-08-02, so the project sees ongoing work. The release list supports that: UnitsNet/5.75.1 and the 6.0.0-pre020 pre-releases all carry July 2026 timestamps. The README also documents a maintenance branch, maintenance/v5, which receives backported units until v6 becomes stable. That is a deliberate split, and it means you can stay on 5.x without being frozen out of new unit definitions.

Upgrade cost is documented rather than implied. The README links two wiki pages, "Upgrading from 5.x to 6.x" and "Upgrading from 4.x to 5.x". Read the relevant one before changing your package reference, because the extension package story alone differs between the classic UnitsNet.NumberExtensions and the C# 14 UnitsNet.NumberExtensions.CS14 variant, and the syntax differs with it.

The licence is MIT-0. That is a permissive licence with no attribution requirement, which is unusual among open source projects and removes the licence-header question from redistribution entirely. This is not legal advice; if your organisation has a policy on which licences are acceptable, MIT-0 is the identifier to check against it.

Editorial conclusion

Adopt UnitsNet if your C# codebase passes raw doubles between layers and you want compile-time separation between mass, force and length. Do not adopt it if you need a unit library outside .NET, or if you cannot tolerate a pre-release branch, because master now targets v6 while new units are backported to maintenance/v5. Before upgrading, read the wiki page Upgrading from 5.x to 6.x and check whether your project references UnitsNet.NumberExtensions or the CS14 variant, since the two use different syntax.

Frequently asked questions

How do I install UnitsNet in a C# project?

The README gives a single CLI command, dotnet add package UnitsNet, or you can install from the NuGet Gallery page linked in the README. Build targets listed are .NET Standard 2.0, .NET 8.0, .NET 9.0 and .NET 10.0 preview.

Why does UnitsNet throw AmbiguousUnitParseException?

Some units of a quantity share the same abbreviation, so Parse cannot know which unit was intended. The README's example is Length.Parse("1 pt"), which throws AmbiguousUnitParseException with the message Cannot parse "pt". The README states there is no built-in way to avoid this; you either prevent such input or write your own parser with knowledge of preferred units.

Does UnitsNet work outside .NET, for example in JavaScript?

The package is a .NET library; the build targets listed in the README are .NET Standard 2.0, .NET 8.0, .NET 9.0 and .NET 10.0 preview. The README does have a section titled Units.NET on other platforms, but the documentation does not describe a JavaScript package.

How does UnitsNet serialize quantities to JSON?

The repository contains two serialization packages, UnitsNet.Serialization.JsonNet and UnitsNet.Serialization.SystemTextJson, each with its own test project. The README's table of contents lists a section on serializing to JSON, XML and more.

Official sources

  1. angularsen/UnitsNet on GitHub
  2. License: MIT-0
  3. Project website
  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/angularsen-unitsnet.svg)](https://hysenlabs.com/projects/angularsen-unitsnet)