diff --git a/CodeGen/Generators/UnitsNetGen/ProjectGenerator.cs b/CodeGen/Generators/UnitsNetGen/ProjectGenerator.cs
new file mode 100644
index 0000000000..4b4c0e0a02
--- /dev/null
+++ b/CodeGen/Generators/UnitsNetGen/ProjectGenerator.cs
@@ -0,0 +1,86 @@
+using System;
+using CodeGen.Helpers;
+using CodeGen.JsonTypes;
+
+namespace CodeGen.Generators.UnitsNetGen
+{
+ internal class ProjectGenerator : GeneratorBase
+ {
+ private readonly Quantity _quantity;
+
+ public ProjectGenerator(Quantity quantity)
+ {
+ _quantity = quantity ?? throw new ArgumentNullException(nameof(quantity));
+ }
+
+ public string Generate()
+ {
+ Writer.WL($@"
+
+
+
+ UnitsNet.Duration
+ 5.0.0-rc008
+ Andreas Gullberg Larsen
+ Units.NET {_quantity.Name}
+ Adds {_quantity.Name} units for Units.NET.
+ Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com).
+ true
+ https://github.com/angularsen/UnitsNet
+ logo-32.png
+ https://raw.githubusercontent.com/angularsen/UnitsNet/ce85185429be345d77eb2ce09c99d59cc9ab8aed/Docs/Images/logo-32.png
+ https://github.com/angularsen/UnitsNet
+ MIT
+ false
+ {_quantity.Name.ToLower()} unit units quantity quantities measurement si metric imperial abbreviation abbreviations convert conversion parse immutable
+ README.md
+
+
+
+
+ 5.0.0.0
+ latest
+ enable
+ UnitsNet
+ netstandard2.0
+ {HashGuid.ToHashGuid(_quantity.Name):B}
+
+
+
+
+ true
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+
+
+
+
+ ../../UnitsNet.snk
+ false
+ true
+ UnitsNet.{_quantity.Name}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+");
+
+ return Writer.ToString();
+ }
+ }
+}
diff --git a/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs b/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
index b4c1faea65..240b614c00 100644
--- a/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
+++ b/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
@@ -220,6 +220,9 @@ private void GenerateStaticProperties()
///
public static QuantityInfo<{_unitEnumName}> Info {{ get; }}
+ ///
+ static QuantityInfo IQuantity.Info => Info;
+
///
/// The of this quantity.
///
diff --git a/CodeGen/Generators/UnitsNetGen/SolutionGenerator.cs b/CodeGen/Generators/UnitsNetGen/SolutionGenerator.cs
new file mode 100644
index 0000000000..6e684c21dd
--- /dev/null
+++ b/CodeGen/Generators/UnitsNetGen/SolutionGenerator.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Text;
+using CodeGen.Helpers;
+using CodeGen.JsonTypes;
+
+namespace CodeGen.Generators.UnitsNetGen
+{
+ internal class SolutionGenerator : GeneratorBase
+ {
+ private readonly Quantity[] _quantities;
+ private readonly Guid _globalGuid = new("71d2836c-ed62-4b76-ba38-e15badcca916"); // Randomly generated guids.
+ private readonly Guid _solutionGuid = new("1f322b1f-1612-4e69-a31f-cb46bf87ec3e");
+
+ public SolutionGenerator(Quantity[] quantities)
+ {
+ _quantities = quantities;
+ }
+
+ public string Generate()
+ {
+ StringBuilder sb = new();
+ Writer.WL($@"
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29609.76
+MinimumVisualStudioVersion = 10.0.40219.1");
+
+ foreach (var quantity in _quantities)
+ {
+ var projectGuid = HashGuid.ToHashGuid(quantity.Name);
+ var projectName = $"UnitsNet.{quantity.Name}";
+ Writer.WL($@"
+Project(""{_globalGuid:B}"") = ""{projectName}"", ""UnitsNet.Modular\{projectName}\{projectName}.csproj"", ""{projectGuid:B}""
+EndProject");
+ sb.Append($"{{{projectGuid}}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n");
+ sb.Append($"{{{projectGuid}}}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n");
+ sb.Append($"{{{projectGuid}}}.Debug|Any CPU.Deploy.0 = Debug|Any CPU\r\n");
+ sb.Append($"{{{projectGuid}}}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n");
+ sb.Append($"{{{projectGuid}}}.Release|Any CPU.Build.0 = Release|Any CPU\r\n");
+ sb.Append($"{{{projectGuid}}}.Release|Any CPU.Deploy.0 = Release|Any CPU\r\n");
+ }
+
+ Writer.WL(@"Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution");
+
+ Writer.WL(sb.ToString());
+
+ Writer.WL($@" EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {_solutionGuid:B}
+ EndGlobalSection
+EndGlobal
+");
+ return Writer.ToString();
+ }
+ }
+}
diff --git a/CodeGen/Generators/UnitsNetModularGen/ProjectGenerator.cs b/CodeGen/Generators/UnitsNetModularGen/ProjectGenerator.cs
new file mode 100644
index 0000000000..088f8672db
--- /dev/null
+++ b/CodeGen/Generators/UnitsNetModularGen/ProjectGenerator.cs
@@ -0,0 +1,86 @@
+using System;
+using CodeGen.Helpers;
+using CodeGen.JsonTypes;
+
+namespace CodeGen.Generators.UnitsNetModularGen
+{
+ internal class ProjectGenerator : GeneratorBase
+ {
+ private readonly Quantity _quantity;
+
+ public ProjectGenerator(Quantity quantity)
+ {
+ _quantity = quantity ?? throw new ArgumentNullException(nameof(quantity));
+ }
+
+ public string Generate()
+ {
+ Writer.WL($@"
+
+
+
+ UnitsNet.Duration
+ 5.0.0-rc008
+ Andreas Gullberg Larsen
+ Units.NET {_quantity.Name}
+ Adds {_quantity.Name} units for Units.NET.
+ Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com).
+ true
+ https://github.com/angularsen/UnitsNet
+ logo-32.png
+ https://raw.githubusercontent.com/angularsen/UnitsNet/ce85185429be345d77eb2ce09c99d59cc9ab8aed/Docs/Images/logo-32.png
+ https://github.com/angularsen/UnitsNet
+ MIT
+ false
+ {_quantity.Name.ToLower()} unit units quantity quantities measurement si metric imperial abbreviation abbreviations convert conversion parse immutable
+ README.md
+
+
+
+
+ 5.0.0.0
+ latest
+ enable
+ UnitsNet
+ netstandard2.0
+ {HashGuid.ToHashGuid(_quantity.Name):B}
+
+
+
+
+ true
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+
+
+
+
+ ../../UnitsNet.snk
+ false
+ true
+ UnitsNet.{_quantity.Name}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+");
+
+ return Writer.ToString();
+ }
+ }
+}
diff --git a/CodeGen/Generators/UnitsNetModularGen/QuantityGenerator.cs b/CodeGen/Generators/UnitsNetModularGen/QuantityGenerator.cs
new file mode 100644
index 0000000000..29227316bf
--- /dev/null
+++ b/CodeGen/Generators/UnitsNetModularGen/QuantityGenerator.cs
@@ -0,0 +1,1190 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Linq;
+using CodeGen.Helpers;
+using CodeGen.JsonTypes;
+
+namespace CodeGen.Generators.UnitsNetModularGen
+{
+ internal class QuantityGenerator : GeneratorBase
+ {
+ private readonly Quantity _quantity;
+
+ private readonly bool _isDimensionless;
+ private readonly string _unitEnumName;
+ private readonly string _valueType;
+ private readonly Unit _baseUnit;
+
+ public QuantityGenerator(Quantity quantity)
+ {
+ _quantity = quantity ?? throw new ArgumentNullException(nameof(quantity));
+
+ _baseUnit = quantity.Units.FirstOrDefault(u => u.SingularName == _quantity.BaseUnit) ??
+ throw new ArgumentException($"No unit found with SingularName equal to BaseUnit [{_quantity.BaseUnit}]. This unit must be defined.",
+ nameof(quantity));
+
+ _valueType = quantity.ValueType;
+ _unitEnumName = $"{quantity.Name}Unit";
+
+ BaseDimensions baseDimensions = quantity.BaseDimensions;
+ _isDimensionless = baseDimensions is { L: 0, M: 0, T: 0, I: 0, Θ: 0, N: 0, J: 0 };
+ }
+
+ public string Generate()
+ {
+ Writer.WL(GeneratedFileHeader);
+ Writer.WL(@"
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+using System.Runtime.Serialization;
+using UnitsNet.InternalHelpers;
+using UnitsNet.Units;
+
+#nullable enable
+
+// ReSharper disable once CheckNamespace
+
+namespace UnitsNet
+{");
+ Writer.WL($@"
+ ///
+ ///
+ /// {_quantity.XmlDocSummary}
+ /// ");
+
+ Writer.WLCondition(_quantity.XmlDocRemarks.HasText(), $@"
+ ///
+ /// {_quantity.XmlDocRemarks}
+ /// ");
+
+ Writer.WLIfText(1, GetObsoleteAttributeOrNull(_quantity));
+ Writer.W(@$"
+ [DataContract]
+ public readonly partial struct {_quantity.Name} : {(_quantity.GenerateArithmetic ? "IArithmeticQuantity" : "IQuantity")}<{_quantity.Name}, {_unitEnumName}, {_quantity.ValueType}>, ");
+ if (_quantity.ValueType == "decimal")
+ {
+ Writer.W("IDecimalQuantity, ");
+ }
+
+ Writer.WL($"IEquatable<{_quantity.Name}>, IComparable, IComparable<{_quantity.Name}>, IConvertible, IFormattable");
+ Writer.WL($@"
+ {{
+ ///
+ /// The numeric value this quantity was constructed with.
+ ///
+ [DataMember(Name = ""Value"", Order = 0)]
+ private readonly {_quantity.ValueType} _value;
+
+ ///
+ /// The unit this quantity was constructed with.
+ ///
+ [DataMember(Name = ""Unit"", Order = 1)]
+ private readonly {_unitEnumName}? _unit;
+");
+ GenerateStaticConstructor();
+ GenerateInstanceConstructors();
+ GenerateStaticProperties();
+ GenerateProperties();
+ GenerateConversionProperties();
+ GenerateStaticMethods();
+ GenerateStaticFactoryMethods();
+ GenerateStaticParseMethods();
+ GenerateArithmeticOperators();
+ GenerateEqualityAndComparison();
+ GenerateConversionMethods();
+ GenerateToString();
+ GenerateIConvertibleMethods();
+
+ Writer.WL($@"
+ }}
+}}");
+ return Writer.ToString();
+ }
+
+ private void GenerateStaticConstructor()
+ {
+ BaseDimensions baseDimensions = _quantity.BaseDimensions;
+ Writer.WL($@"
+ static {_quantity.Name}()
+ {{");
+ Writer.WL(_isDimensionless ? $@"
+ BaseDimensions = BaseDimensions.Dimensionless;" : $@"
+ BaseDimensions = new BaseDimensions({baseDimensions.L}, {baseDimensions.M}, {baseDimensions.T}, {baseDimensions.I}, {baseDimensions.Θ}, {baseDimensions.N}, {baseDimensions.J});");
+
+ Writer.WL($@"
+ BaseUnit = {_unitEnumName}.{_quantity.BaseUnit};
+ Units = Enum.GetValues(typeof({_unitEnumName})).Cast<{_unitEnumName}>().ToArray();
+ Zero = new {_quantity.Name}(0, BaseUnit);
+ Info = new QuantityInfo<{_unitEnumName}>(""{_quantity.Name}"",
+ new UnitInfo<{_unitEnumName}>[]
+ {{");
+
+ foreach (Unit unit in _quantity.Units)
+ {
+ BaseUnits? baseUnits = unit.BaseUnits;
+ if (baseUnits == null)
+ {
+ Writer.WL($@"
+ new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, ""{unit.PluralName}"", BaseUnits.Undefined),");
+ }
+ else
+ {
+ var baseUnitsCtorArgs = string.Join(", ",
+ new[]
+ {
+ baseUnits.L != null ? $"length: LengthUnit.{baseUnits.L}" : null,
+ baseUnits.M != null ? $"mass: MassUnit.{baseUnits.M}" : null,
+ baseUnits.T != null ? $"time: DurationUnit.{baseUnits.T}" : null,
+ baseUnits.I != null ? $"current: ElectricCurrentUnit.{baseUnits.I}" : null,
+ baseUnits.Θ != null ? $"temperature: TemperatureUnit.{baseUnits.Θ}" : null,
+ baseUnits.N != null ? $"amount: AmountOfSubstanceUnit.{baseUnits.N}" : null,
+ baseUnits.J != null ? $"luminousIntensity: LuminousIntensityUnit.{baseUnits.J}" : null
+ }.Where(str => str != null));
+
+ Writer.WL($@"
+ new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, ""{unit.PluralName}"", new BaseUnits({baseUnitsCtorArgs})),");
+ }
+ }
+
+ Writer.WL($@"
+ }},
+ BaseUnit, Zero, BaseDimensions);
+
+ DefaultConversionFunctions = new UnitConverter();
+ RegisterDefaultConversions(DefaultConversionFunctions);
+ }}
+");
+ }
+
+ private void GenerateInstanceConstructors()
+ {
+ Writer.WL($@"
+ ///
+ /// Creates the quantity with the given numeric value and unit.
+ ///
+ /// The numeric value to construct this quantity with.
+ /// The unit representation to construct this quantity with.
+ /// If value is NaN or Infinity.
+ public {_quantity.Name}({_quantity.ValueType} value, {_unitEnumName} unit)
+ {{");
+ Writer.WL(_quantity.ValueType == "double"
+ ? @"
+ _value = Guard.EnsureValidNumber(value, nameof(value));"
+ : @"
+ _value = value;");
+ Writer.WL($@"
+ _unit = unit;
+ }}
+");
+ }
+
+ private void GenerateStaticProperties()
+ {
+ Writer.WL($@"
+ #region Static Properties
+
+ ///
+ /// The containing the default generated conversion functions for instances.
+ ///
+ public static UnitConverter DefaultConversionFunctions {{ get; }}
+
+ ///
+ public static QuantityInfo<{_unitEnumName}> Info {{ get; }}
+
+ ///
+ static QuantityInfo IQuantity.Info => Info;
+
+ ///
+ /// The of this quantity.
+ ///
+ public static BaseDimensions BaseDimensions {{ get; }}
+
+ ///
+ /// The base unit of {_quantity.Name}, which is {_quantity.BaseUnit}. All conversions go via this value.
+ ///
+ public static {_unitEnumName} BaseUnit {{ get; }}
+
+ ///
+ /// All units of measurement for the {_quantity.Name} quantity.
+ ///
+ public static {_unitEnumName}[] Units {{ get; }}
+
+ ///
+ /// Gets an instance of this quantity with a value of 0 in the base unit {_quantity.BaseUnit}.
+ ///
+ public static {_quantity.Name} Zero {{ get; }}
+");
+
+ if (_quantity.GenerateArithmetic)
+ {
+ Writer.WL($@"
+ ///
+ public static {_quantity.Name} AdditiveIdentity => Zero;
+");
+ }
+
+ Writer.WL($@"
+ #endregion
+ ");
+ }
+
+ private void GenerateProperties()
+ {
+ Writer.WL($@"
+ #region Properties
+
+ ///
+ /// The numeric value this quantity was constructed with.
+ ///
+ public {_valueType} Value => _value;
+");
+
+ Writer.WL(@"
+ ///
+ QuantityValue IQuantity.Value => _value;
+");
+ // Need to provide explicit interface implementation for decimal quantities like Information
+ if (_quantity.ValueType == "decimal")
+ Writer.WL(@"
+ ///
+ decimal IDecimalQuantity.Value => _value;
+");
+
+ Writer.WL($@"
+ Enum IQuantity.Unit => Unit;
+
+ ///
+ public {_unitEnumName} Unit => _unit.GetValueOrDefault(BaseUnit);
+
+ ///
+ public QuantityInfo<{_unitEnumName}> QuantityInfo => Info;
+
+ ///
+ QuantityInfo IQuantity.QuantityInfo => Info;
+
+ ///
+ /// The of this quantity.
+ ///
+ public BaseDimensions Dimensions => {_quantity.Name}.BaseDimensions;
+
+ #endregion
+");
+ }
+
+ private void GenerateConversionProperties()
+ {
+ Writer.WL(@"
+ #region Conversion Properties
+");
+ foreach (Unit unit in _quantity.Units)
+ {
+ if (unit.SkipConversionGeneration) continue;
+
+ Writer.WL($@"
+ ///
+ /// Gets a value of this quantity converted into
+ /// ");
+ Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit));
+ Writer.WL($@"
+ public {_quantity.ValueType} {unit.PluralName} => As({_unitEnumName}.{unit.SingularName});
+");
+ }
+
+ Writer.WL(@"
+
+ #endregion
+");
+ }
+
+ private void GenerateStaticMethods()
+ {
+ Writer.WL($@"
+
+ #region Static Methods
+
+ ///
+ /// Registers the default conversion functions in the given instance.
+ ///
+ /// The to register the default conversion functions in.
+ internal static void RegisterDefaultConversions(UnitConverter unitConverter)
+ {{
+ // Register in unit converter: {_quantity.Name}Unit -> BaseUnit");
+
+ foreach (Unit unit in _quantity.Units)
+ {
+ if (unit.SingularName == _quantity.BaseUnit) continue;
+
+ Writer.WL($@"
+ unitConverter.SetConversionFunction<{_quantity.Name}>({_quantity.Name}Unit.{unit.SingularName}, {_unitEnumName}.{_quantity.BaseUnit}, quantity => quantity.ToUnit({_unitEnumName}.{_quantity.BaseUnit}));");
+ }
+
+ Writer.WL();
+ Writer.WL($@"
+
+ // Register in unit converter: BaseUnit <-> BaseUnit
+ unitConverter.SetConversionFunction<{_quantity.Name}>({_unitEnumName}.{_quantity.BaseUnit}, {_unitEnumName}.{_quantity.BaseUnit}, quantity => quantity);
+
+ // Register in unit converter: BaseUnit -> {_quantity.Name}Unit");
+
+ foreach (Unit unit in _quantity.Units)
+ {
+ if (unit.SingularName == _quantity.BaseUnit) continue;
+
+ Writer.WL($@"
+ unitConverter.SetConversionFunction<{_quantity.Name}>({_unitEnumName}.{_quantity.BaseUnit}, {_quantity.Name}Unit.{unit.SingularName}, quantity => quantity.ToUnit({_quantity.Name}Unit.{unit.SingularName}));");
+ }
+
+ Writer.WL($@"
+ }}
+
+ internal static void MapGeneratedLocalizations(UnitAbbreviationsCache unitAbbreviationsCache)
+ {{");
+ foreach(Unit unit in _quantity.Units)
+ {
+ foreach(Localization localization in unit.Localization)
+ {
+ // All units must have a unit abbreviation, so fallback to "" for units with no abbreviations defined in JSON
+ var abbreviationParams = localization.Abbreviations.Any() ?
+ string.Join(", ", localization.Abbreviations.Select(abbr => $@"""{abbr}""")) :
+ $@"""""";
+
+ Writer.WL($@"
+ unitAbbreviationsCache.PerformAbbreviationMapping({_unitEnumName}.{unit.SingularName}, new CultureInfo(""{localization.Culture}""), false, {unit.AllowAbbreviationLookup.ToString().ToLower()}, new string[]{{{abbreviationParams}}});");
+ }
+ }
+
+ Writer.WL($@"
+ }}
+
+ ///
+ /// Get unit abbreviation string.
+ ///
+ /// Unit to get abbreviation for.
+ /// Unit abbreviation string.
+ public static string GetAbbreviation({_unitEnumName} unit)
+ {{
+ return GetAbbreviation(unit, null);
+ }}
+
+ ///
+ /// Get unit abbreviation string.
+ ///
+ /// Unit to get abbreviation for.
+ /// Unit abbreviation string.
+ /// Format to use for localization. Defaults to if null.
+ public static string GetAbbreviation({_unitEnumName} unit, IFormatProvider? provider)
+ {{
+ return UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit, provider);
+ }}
+
+ #endregion
+");
+ }
+
+ private void GenerateStaticFactoryMethods()
+ {
+ Writer.WL(@"
+ #region Static Factory Methods
+");
+ foreach (Unit unit in _quantity.Units)
+ {
+ if (unit.SkipConversionGeneration) continue;
+
+ var valueParamName = unit.PluralName.ToLowerInvariant();
+ Writer.WL($@"
+ ///
+ /// Creates a from .
+ ///
+ /// If value is NaN or Infinity.");
+ Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit));
+ Writer.WL($@"
+ public static {_quantity.Name} From{unit.PluralName}(QuantityValue {valueParamName})
+ {{
+ {_valueType} value = ({_valueType}) {valueParamName};
+ return new {_quantity.Name}(value, {_unitEnumName}.{unit.SingularName});
+ }}
+");
+ }
+
+ Writer.WL($@"
+ ///
+ /// Dynamically convert from value and unit enum to .
+ ///
+ /// Value to convert from.
+ /// Unit to convert from.
+ /// {_quantity.Name} unit value.
+ public static {_quantity.Name} From(QuantityValue value, {_unitEnumName} fromUnit)
+ {{
+ return new {_quantity.Name}(({_valueType})value, fromUnit);
+ }}
+
+ #endregion
+");
+ }
+
+ private void GenerateStaticParseMethods()
+ {
+ Writer.WL($@"
+ #region Static Parse Methods
+
+ ///
+ /// Parse a string with one or two quantities of the format ""<quantity> <unit>"".
+ ///
+ /// String to parse. Typically in the form: {{number}} {{unit}}
+ ///
+ /// Length.Parse(""5.5 m"", new CultureInfo(""en-US""));
+ ///
+ /// The value of 'str' cannot be null.
+ ///
+ /// Expected string to have one or two pairs of quantity and unit in the format
+ /// ""<quantity> <unit>"". Eg. ""5.5 m"" or ""1ft 2in""
+ ///
+ ///
+ /// More than one unit is represented by the specified unit abbreviation.
+ /// Example: Volume.Parse(""1 cup"") will throw, because it can refer to any of
+ /// , and .
+ ///
+ ///
+ /// If anything else goes wrong, typically due to a bug or unhandled case.
+ /// We wrap exceptions in to allow you to distinguish
+ /// Units.NET exceptions from other exceptions.
+ ///
+ public static {_quantity.Name} Parse(string str)
+ {{
+ return Parse(str, null);
+ }}
+
+ ///
+ /// Parse a string with one or two quantities of the format ""<quantity> <unit>"".
+ ///
+ /// String to parse. Typically in the form: {{number}} {{unit}}
+ ///
+ /// Length.Parse(""5.5 m"", new CultureInfo(""en-US""));
+ ///
+ /// The value of 'str' cannot be null.
+ ///
+ /// Expected string to have one or two pairs of quantity and unit in the format
+ /// ""<quantity> <unit>"". Eg. ""5.5 m"" or ""1ft 2in""
+ ///
+ ///
+ /// More than one unit is represented by the specified unit abbreviation.
+ /// Example: Volume.Parse(""1 cup"") will throw, because it can refer to any of
+ /// , and .
+ ///
+ ///
+ /// If anything else goes wrong, typically due to a bug or unhandled case.
+ /// We wrap exceptions in to allow you to distinguish
+ /// Units.NET exceptions from other exceptions.
+ ///
+ /// Format to use when parsing number and unit. Defaults to if null.
+ public static {_quantity.Name} Parse(string str, IFormatProvider? provider)
+ {{
+ return QuantityParser.Default.Parse<{_quantity.Name}, {_unitEnumName}>(
+ str,
+ provider,
+ From);
+ }}
+
+ ///
+ /// Try to parse a string with one or two quantities of the format ""<quantity> <unit>"".
+ ///
+ /// String to parse. Typically in the form: {{number}} {{unit}}
+ /// Resulting unit quantity if successful.
+ ///
+ /// Length.Parse(""5.5 m"", new CultureInfo(""en-US""));
+ ///
+ public static bool TryParse(string? str, out {_quantity.Name} result)
+ {{
+ return TryParse(str, null, out result);
+ }}
+
+ ///
+ /// Try to parse a string with one or two quantities of the format ""<quantity> <unit>"".
+ ///
+ /// String to parse. Typically in the form: {{number}} {{unit}}
+ /// Resulting unit quantity if successful.
+ /// True if successful, otherwise false.
+ ///
+ /// Length.Parse(""5.5 m"", new CultureInfo(""en-US""));
+ ///
+ /// Format to use when parsing number and unit. Defaults to if null.
+ public static bool TryParse(string? str, IFormatProvider? provider, out {_quantity.Name} result)
+ {{
+ return QuantityParser.Default.TryParse<{_quantity.Name}, {_unitEnumName}>(
+ str,
+ provider,
+ From,
+ out result);
+ }}
+
+ ///
+ /// Parse a unit string.
+ ///
+ /// String to parse. Typically in the form: {{number}} {{unit}}
+ ///
+ /// Length.ParseUnit(""m"", new CultureInfo(""en-US""));
+ ///
+ /// The value of 'str' cannot be null.
+ /// Error parsing string.
+ public static {_unitEnumName} ParseUnit(string str)
+ {{
+ return ParseUnit(str, null);
+ }}
+
+ ///
+ /// Parse a unit string.
+ ///
+ /// String to parse. Typically in the form: {{number}} {{unit}}
+ /// Format to use when parsing number and unit. Defaults to if null.
+ ///
+ /// Length.ParseUnit(""m"", new CultureInfo(""en-US""));
+ ///
+ /// The value of 'str' cannot be null.
+ /// Error parsing string.
+ public static {_unitEnumName} ParseUnit(string str, IFormatProvider? provider)
+ {{
+ return UnitParser.Default.Parse<{_unitEnumName}>(str, provider);
+ }}
+
+ ///
+ public static bool TryParseUnit(string str, out {_unitEnumName} unit)
+ {{
+ return TryParseUnit(str, null, out unit);
+ }}
+
+ ///
+ /// Parse a unit string.
+ ///
+ /// String to parse. Typically in the form: {{number}} {{unit}}
+ /// The parsed unit if successful.
+ /// True if successful, otherwise false.
+ ///
+ /// Length.TryParseUnit(""m"", new CultureInfo(""en-US""));
+ ///
+ /// Format to use when parsing number and unit. Defaults to if null.
+ public static bool TryParseUnit(string str, IFormatProvider? provider, out {_unitEnumName} unit)
+ {{
+ return UnitParser.Default.TryParse<{_unitEnumName}>(str, provider, out unit);
+ }}
+
+ #endregion
+");
+ }
+
+ private void GenerateArithmeticOperators()
+ {
+ if (!_quantity.GenerateArithmetic) return;
+
+ // Logarithmic units required different arithmetic
+ if (_quantity.Logarithmic)
+ {
+ GenerateLogarithmicArithmeticOperators();
+ return;
+ }
+
+ Writer.WL($@"
+ #region Arithmetic Operators
+
+ /// Negate the value.
+ public static {_quantity.Name} operator -({_quantity.Name} right)
+ {{
+ return new {_quantity.Name}(-right.Value, right.Unit);
+ }}
+
+ /// Get from adding two .
+ public static {_quantity.Name} operator +({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return new {_quantity.Name}(left.Value + right.ToUnit(left.Unit).Value, left.Unit);
+ }}
+
+ /// Get from subtracting two .
+ public static {_quantity.Name} operator -({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return new {_quantity.Name}(left.Value - right.ToUnit(left.Unit).Value, left.Unit);
+ }}
+
+ /// Get from multiplying value and .
+ public static {_quantity.Name} operator *({_valueType} left, {_quantity.Name} right)
+ {{
+ return new {_quantity.Name}(left * right.Value, right.Unit);
+ }}
+
+ /// Get from multiplying value and .
+ public static {_quantity.Name} operator *({_quantity.Name} left, {_valueType} right)
+ {{
+ return new {_quantity.Name}(left.Value * right, left.Unit);
+ }}
+
+ /// Get from dividing by value.
+ public static {_quantity.Name} operator /({_quantity.Name} left, {_valueType} right)
+ {{
+ return new {_quantity.Name}(left.Value / right, left.Unit);
+ }}
+
+ /// Get ratio value from dividing by .
+ public static {_quantity.ValueType} operator /({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return left.{_baseUnit.PluralName} / right.{_baseUnit.PluralName};
+ }}
+
+ #endregion
+");
+ }
+
+ private void GenerateLogarithmicArithmeticOperators()
+ {
+ var scalingFactor = _quantity.LogarithmicScalingFactor;
+ // Most logarithmic operators need a simple scaling factor of 10. However, certain units such as voltage ratio need to use 20 instead.
+ var x = (10 * scalingFactor).ToString();
+ Writer.WL($@"
+ #region Logarithmic Arithmetic Operators
+
+ /// Negate the value.
+ public static {_quantity.Name} operator -({_quantity.Name} right)
+ {{
+ return new {_quantity.Name}(-right.Value, right.Unit);
+ }}
+
+ /// Get from logarithmic addition of two .
+ public static {_quantity.Name} operator +({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ // Logarithmic addition
+ // Formula: {x} * log10(10^(x/{x}) + 10^(y/{x}))
+ return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, left.Value / {x}) + Math.Pow(10, right.ToUnit(left.Unit).Value / {x})), left.Unit);
+ }}
+
+ /// Get from logarithmic subtraction of two .
+ public static {_quantity.Name} operator -({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ // Logarithmic subtraction
+ // Formula: {x} * log10(10^(x/{x}) - 10^(y/{x}))
+ return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, left.Value / {x}) - Math.Pow(10, right.ToUnit(left.Unit).Value / {x})), left.Unit);
+ }}
+
+ /// Get from logarithmic multiplication of value and .
+ public static {_quantity.Name} operator *({_valueType} left, {_quantity.Name} right)
+ {{
+ // Logarithmic multiplication = addition
+ return new {_quantity.Name}(left + right.Value, right.Unit);
+ }}
+
+ /// Get from logarithmic multiplication of value and .
+ public static {_quantity.Name} operator *({_quantity.Name} left, double right)
+ {{
+ // Logarithmic multiplication = addition
+ return new {_quantity.Name}(left.Value + ({_valueType})right, left.Unit);
+ }}
+
+ /// Get from logarithmic division of by value.
+ public static {_quantity.Name} operator /({_quantity.Name} left, double right)
+ {{
+ // Logarithmic division = subtraction
+ return new {_quantity.Name}(left.Value - ({_valueType})right, left.Unit);
+ }}
+
+ /// Get ratio value from logarithmic division of by .
+ public static double operator /({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ // Logarithmic division = subtraction
+ return Convert.ToDouble(left.Value - right.ToUnit(left.Unit).Value);
+ }}
+
+ #endregion
+" );
+ }
+
+ private void GenerateEqualityAndComparison()
+ {
+ Writer.WL($@"
+ #region Equality / IComparable
+
+ /// Returns true if less or equal to.
+ public static bool operator <=({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return left.Value <= right.ToUnit(left.Unit).Value;
+ }}
+
+ /// Returns true if greater than or equal to.
+ public static bool operator >=({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return left.Value >= right.ToUnit(left.Unit).Value;
+ }}
+
+ /// Returns true if less than.
+ public static bool operator <({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return left.Value < right.ToUnit(left.Unit).Value;
+ }}
+
+ /// Returns true if greater than.
+ public static bool operator >({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return left.Value > right.ToUnit(left.Unit).Value;
+ }}
+
+ // We use obsolete attribute to communicate the preferred equality members to use.
+ // CS0809: Obsolete member 'memberA' overrides non-obsolete member 'memberB'.
+ #pragma warning disable CS0809
+
+ /// Indicates strict equality of two quantities, where both and are exactly equal.
+ /// Consider using to check equality across different units and to specify a floating-point number error tolerance.
+ [Obsolete(""Consider using Equals(Angle, {_valueType}, ComparisonType) to check equality across different units and to specify a floating-point number error tolerance."")]
+ public static bool operator ==({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return left.Equals(right);
+ }}
+
+ /// Indicates strict inequality of two quantities, where both and are exactly equal.
+ /// Consider using to check equality across different units and to specify a floating-point number error tolerance.
+ [Obsolete(""Consider using Equals(Angle, {_valueType}, ComparisonType) to check equality across different units and to specify a floating-point number error tolerance."")]
+ public static bool operator !=({_quantity.Name} left, {_quantity.Name} right)
+ {{
+ return !(left == right);
+ }}
+
+ ///
+ /// Indicates strict equality of two quantities, where both and are exactly equal.
+ /// Consider using to check equality across different units and to specify a floating-point number error tolerance.
+ [Obsolete(""Consider using Equals(Angle, {_valueType}, ComparisonType) to check equality across different units and to specify a floating-point number error tolerance."")]
+ public override bool Equals(object? obj)
+ {{
+ if (obj is null || !(obj is {_quantity.Name} otherQuantity))
+ return false;
+
+ return Equals(otherQuantity);
+ }}
+
+ ///
+ /// Indicates strict equality of two quantities, where both and are exactly equal.
+ /// Consider using to check equality across different units and to specify a floating-point number error tolerance.
+ [Obsolete(""Consider using Equals(Angle, {_valueType}, ComparisonType) to check equality across different units and to specify a floating-point number error tolerance."")]
+ public bool Equals({_quantity.Name} other)
+ {{
+ return new {{ Value, Unit }}.Equals(new {{ other.Value, other.Unit }});
+ }}
+
+ #pragma warning restore CS0809
+
+ /// Compares the current with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other when converted to the same unit.
+ /// An object to compare with this instance.
+ ///
+ /// is not the same type as this instance.
+ ///
+ /// A value that indicates the relative order of the quantities being compared. The return value has these meanings:
+ ///
+ /// Value Meaning
+ /// - Less than zero This instance precedes in the sort order.
+ /// - Zero This instance occurs in the same position in the sort order as .
+ /// - Greater than zero This instance follows in the sort order.
+ ///
+ ///
+ public int CompareTo(object? obj)
+ {{
+ if (obj is null) throw new ArgumentNullException(nameof(obj));
+ if (!(obj is {_quantity.Name} otherQuantity)) throw new ArgumentException(""Expected type {_quantity.Name}."", nameof(obj));
+
+ return CompareTo(otherQuantity);
+ }}
+
+ /// Compares the current with another and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other when converted to the same unit.
+ /// A quantity to compare with this instance.
+ /// A value that indicates the relative order of the quantities being compared. The return value has these meanings:
+ ///
+ /// Value Meaning
+ /// - Less than zero This instance precedes in the sort order.
+ /// - Zero This instance occurs in the same position in the sort order as .
+ /// - Greater than zero This instance follows in the sort order.
+ ///
+ ///
+ public int CompareTo({_quantity.Name} other)
+ {{
+ return _value.CompareTo(other.ToUnit(this.Unit).Value);
+ }}
+
+ ///
+ ///
+ /// Compare equality to another {_quantity.Name} within the given absolute or relative tolerance.
+ ///
+ ///
+ /// Relative tolerance is defined as the maximum allowable absolute difference between this quantity's value and
+ /// as a percentage of this quantity's value. will be converted into
+ /// this quantity's unit for comparison. A relative tolerance of 0.01 means the absolute difference must be within +/- 1% of
+ /// this quantity's value to be considered equal.
+ ///
+ /// In this example, the two quantities will be equal if the value of b is within +/- 1% of a (0.02m or 2cm).
+ ///
+ /// var a = Length.FromMeters(2.0);
+ /// var b = Length.FromInches(50.0);
+ /// a.Equals(b, 0.01, ComparisonType.Relative);
+ ///
+ ///
+ ///
+ ///
+ /// Absolute tolerance is defined as the maximum allowable absolute difference between this quantity's value and
+ /// as a fixed number in this quantity's unit. will be converted into
+ /// this quantity's unit for comparison.
+ ///
+ /// In this example, the two quantities will be equal if the value of b is within 0.01 of a (0.01m or 1cm).
+ ///
+ /// var a = Length.FromMeters(2.0);
+ /// var b = Length.FromInches(50.0);
+ /// a.Equals(b, 0.01, ComparisonType.Absolute);
+ ///
+ ///
+ ///
+ ///
+ /// Note that it is advised against specifying zero difference, due to the nature
+ /// of floating-point operations and using {_valueType} internally.
+ ///
+ ///
+ /// The other quantity to compare to.
+ /// The absolute or relative tolerance value. Must be greater than or equal to 0.
+ /// The comparison type: either relative or absolute.
+ /// True if the absolute difference between the two values is not greater than the specified relative or absolute tolerance.
+ public bool Equals({_quantity.Name} other, {_quantity.ValueType} tolerance, ComparisonType comparisonType)
+ {{
+ if (tolerance < 0)
+ throw new ArgumentOutOfRangeException(""tolerance"", ""Tolerance must be greater than or equal to 0."");
+
+ {_quantity.ValueType} thisValue = this.Value;
+ {_quantity.ValueType} otherValueInThisUnits = other.As(this.Unit);
+
+ return UnitsNet.Comparison.Equals(thisValue, otherValueInThisUnits, tolerance, comparisonType);
+ }}
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ /// A hash code for the current {_quantity.Name}.
+ public override int GetHashCode()
+ {{
+ return new {{ Info.Name, Value, Unit }}.GetHashCode();
+ }}
+
+ #endregion
+");
+ }
+
+ private void GenerateConversionMethods()
+ {
+ Writer.WL($@"
+ #region Conversion Methods
+
+ ///
+ /// Convert to the unit representation .
+ ///
+ /// Value converted to the specified unit.
+ public {_quantity.ValueType} As({_unitEnumName} unit)
+ {{
+ if (Unit == unit)
+ return Value;
+
+ return ToUnit(unit).Value;
+ }}
+");
+
+ if (_quantity.ValueType == "decimal")
+ {
+ Writer.WL($@"
+
+ double IQuantity<{_unitEnumName}>.As({_unitEnumName} unit)
+ {{
+ return (double)As(unit);
+ }}
+");
+ }
+
+ Writer.WL($@"
+ ///
+ double IQuantity.As(Enum unit)
+ {{
+ if (!(unit is {_unitEnumName} typedUnit))
+ throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit));
+
+ return (double)As(typedUnit);
+ }}
+
+ ///
+ /// Converts this {_quantity.Name} to another {_quantity.Name} with the unit representation .
+ ///
+ /// The unit to convert to.
+ /// A {_quantity.Name} with the specified unit.
+ public {_quantity.Name} ToUnit({_unitEnumName} unit)
+ {{
+ return ToUnit(unit, DefaultConversionFunctions);
+ }}
+
+ ///
+ /// Converts this to another using the given with the unit representation .
+ ///
+ /// The unit to convert to.
+ /// The to use for the conversion.
+ /// A {_quantity.Name} with the specified unit.
+ public {_quantity.Name} ToUnit({_unitEnumName} unit, UnitConverter unitConverter)
+ {{
+ if (TryToUnit(unit, out var converted))
+ {{
+ // Try to convert using the auto-generated conversion methods.
+ return converted!.Value;
+ }}
+ else if (unitConverter.TryGetConversionFunction((typeof({_quantity.Name}), Unit, typeof({_quantity.Name}), unit), out var conversionFunction))
+ {{
+ // See if the unit converter has an extensibility conversion registered.
+ return ({_quantity.Name})conversionFunction(this);
+ }}
+ else if (Unit != BaseUnit)
+ {{
+ // Conversion to requested unit NOT found. Try to convert to BaseUnit, and then from BaseUnit to requested unit.
+ var inBaseUnits = ToUnit(BaseUnit);
+ return inBaseUnits.ToUnit(unit);
+ }}
+ else
+ {{
+ // No possible conversion
+ throw new NotImplementedException($""Can not convert {{Unit}} to {{unit}}."");
+ }}
+ }}
+
+ ///
+ /// Attempts to convert this to another with the unit representation .
+ ///
+ /// The unit to convert to.
+ /// The converted in , if successful.
+ /// True if successful, otherwise false.
+ private bool TryToUnit({_quantity.Name}Unit unit, [NotNullWhen(true)] out {_quantity.Name}? converted)
+ {{
+ if (Unit == unit)
+ {{
+ converted = this;
+ return true;
+ }}
+
+ {_quantity.Name}? convertedOrNull = (Unit, unit) switch
+ {{
+ // {_quantity.Name}Unit -> BaseUnit");
+
+ foreach (Unit unit in _quantity.Units)
+ {
+ if (unit.SingularName == _quantity.BaseUnit) continue;
+
+ var func = unit.FromUnitToBaseFunc.Replace("{x}", "_value");
+ Writer.WL($@"
+ ({_quantity.Name}Unit.{unit.SingularName}, {_unitEnumName}.{_quantity.BaseUnit}) => new {_quantity.Name}({func}, {_unitEnumName}.{_quantity.BaseUnit}),");
+ }
+
+ Writer.WL();
+ Writer.WL($@"
+
+ // BaseUnit -> {_quantity.Name}Unit");
+ foreach(Unit unit in _quantity.Units)
+ {
+ if (unit.SingularName == _quantity.BaseUnit) continue;
+
+ var func = unit.FromBaseToUnitFunc.Replace("{x}", "_value");
+ Writer.WL($@"
+ ({_unitEnumName}.{_quantity.BaseUnit}, {_quantity.Name}Unit.{unit.SingularName}) => new {_quantity.Name}({func}, {_quantity.Name}Unit.{unit.SingularName}),");
+ }
+
+ Writer.WL();
+ Writer.WL($@"
+ _ => null
+ }};
+
+ if (convertedOrNull is null)
+ {{
+ converted = default;
+ return false;
+ }}
+
+ converted = convertedOrNull.Value;
+ return true;
+ }}
+
+ ///
+ IQuantity IQuantity.ToUnit(Enum unit)
+ {{
+ if (!(unit is {_unitEnumName} typedUnit))
+ throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit));
+
+ return ToUnit(typedUnit, DefaultConversionFunctions);
+ }}
+
+ ///
+ IQuantity<{_unitEnumName}> IQuantity<{_unitEnumName}>.ToUnit({_unitEnumName} unit) => ToUnit(unit);
+
+ #endregion
+");
+ }
+
+ private void GenerateToString()
+ {
+ Writer.WL($@"
+ #region ToString Methods
+
+ ///
+ /// Gets the default string representation of value and unit.
+ ///
+ /// String representation.
+ public override string ToString()
+ {{
+ return ToString(""g"");
+ }}
+
+ ///
+ /// Gets the default string representation of value and unit using the given format provider.
+ ///
+ /// String representation.
+ /// Format to use for localization and number formatting. Defaults to if null.
+ public string ToString(IFormatProvider? provider)
+ {{
+ return ToString(""g"", provider);
+ }}
+
+ ///
+ ///
+ /// Gets the string representation of this instance in the specified format string using .
+ ///
+ /// The format string.
+ /// The string representation.
+ public string ToString(string? format)
+ {{
+ return ToString(format, CultureInfo.CurrentCulture);
+ }}
+
+ ///
+ ///
+ /// Gets the string representation of this instance in the specified format string using the specified format provider, or if null.
+ ///
+ /// The format string.
+ /// Format to use for localization and number formatting. Defaults to if null.
+ /// The string representation.
+ public string ToString(string? format, IFormatProvider? provider)
+ {{
+ return QuantityFormatter.Format<{_unitEnumName}>(this, format, provider);
+ }}
+
+ #endregion
+" );
+ }
+
+ private void GenerateIConvertibleMethods()
+ {
+ Writer.WL($@"
+ #region IConvertible Methods
+
+ TypeCode IConvertible.GetTypeCode()
+ {{
+ return TypeCode.Object;
+ }}
+
+ bool IConvertible.ToBoolean(IFormatProvider? provider)
+ {{
+ throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to bool is not supported."");
+ }}
+
+ byte IConvertible.ToByte(IFormatProvider? provider)
+ {{
+ return Convert.ToByte(_value);
+ }}
+
+ char IConvertible.ToChar(IFormatProvider? provider)
+ {{
+ throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to char is not supported."");
+ }}
+
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider)
+ {{
+ throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to DateTime is not supported."");
+ }}
+
+ decimal IConvertible.ToDecimal(IFormatProvider? provider)
+ {{
+ return Convert.ToDecimal(_value);
+ }}
+
+ double IConvertible.ToDouble(IFormatProvider? provider)
+ {{
+ return Convert.ToDouble(_value);
+ }}
+
+ short IConvertible.ToInt16(IFormatProvider? provider)
+ {{
+ return Convert.ToInt16(_value);
+ }}
+
+ int IConvertible.ToInt32(IFormatProvider? provider)
+ {{
+ return Convert.ToInt32(_value);
+ }}
+
+ long IConvertible.ToInt64(IFormatProvider? provider)
+ {{
+ return Convert.ToInt64(_value);
+ }}
+
+ sbyte IConvertible.ToSByte(IFormatProvider? provider)
+ {{
+ return Convert.ToSByte(_value);
+ }}
+
+ float IConvertible.ToSingle(IFormatProvider? provider)
+ {{
+ return Convert.ToSingle(_value);
+ }}
+
+ string IConvertible.ToString(IFormatProvider? provider)
+ {{
+ return ToString(""g"", provider);
+ }}
+
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
+ {{
+ if (conversionType == typeof({_quantity.Name}))
+ return this;
+ else if (conversionType == typeof({_unitEnumName}))
+ return Unit;
+ else if (conversionType == typeof(QuantityInfo))
+ return {_quantity.Name}.Info;
+ else if (conversionType == typeof(BaseDimensions))
+ return {_quantity.Name}.BaseDimensions;
+ else
+ throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to {{conversionType}} is not supported."");
+ }}
+
+ ushort IConvertible.ToUInt16(IFormatProvider? provider)
+ {{
+ return Convert.ToUInt16(_value);
+ }}
+
+ uint IConvertible.ToUInt32(IFormatProvider? provider)
+ {{
+ return Convert.ToUInt32(_value);
+ }}
+
+ ulong IConvertible.ToUInt64(IFormatProvider? provider)
+ {{
+ return Convert.ToUInt64(_value);
+ }}
+
+ #endregion");
+ }
+
+ ///
+ private static string? GetObsoleteAttributeOrNull(Quantity quantity) => GetObsoleteAttributeOrNull(quantity.ObsoleteText);
+
+ ///
+ private static string? GetObsoleteAttributeOrNull(Unit unit) => GetObsoleteAttributeOrNull(unit.ObsoleteText);
+
+ ///
+ /// Returns the Obsolete attribute if ObsoleteText has been defined on the JSON input - otherwise returns empty string
+ /// It is up to the consumer to wrap any padding/new lines in order to keep to correct indentation formats
+ ///
+ private static string? GetObsoleteAttributeOrNull(string? obsoleteText) => string.IsNullOrWhiteSpace(obsoleteText)
+ ? null
+ : $"[Obsolete(\"{obsoleteText}\")]";
+ }
+}
diff --git a/CodeGen/Generators/UnitsNetModularGen/SolutionGenerator.cs b/CodeGen/Generators/UnitsNetModularGen/SolutionGenerator.cs
new file mode 100644
index 0000000000..7421cffb4c
--- /dev/null
+++ b/CodeGen/Generators/UnitsNetModularGen/SolutionGenerator.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Text;
+using CodeGen.Helpers;
+using CodeGen.JsonTypes;
+
+namespace CodeGen.Generators.UnitsNetModularGen
+{
+ class SolutionGenerator:GeneratorBase
+ {
+ private readonly Quantity[] _quantities;
+ private readonly string _globalGuid = new Guid("660e8a78-57a3-4365-b7b5-336a552181ce").ToString("B").ToUpperInvariant(); // Randomly generated guids.
+ private readonly string _solutionGuid = new Guid("39648d62-4f58-4c39-9a1c-5a1d884dedab").ToString("B").ToUpperInvariant();
+
+ public SolutionGenerator(Quantity[] quantities)
+ {
+ _quantities = quantities;
+ }
+
+ public string Generate()
+ {
+ StringBuilder sb = new();
+ Writer.WL($@"
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29609.76
+MinimumVisualStudioVersion = 10.0.40219.1");
+
+ foreach (var quantity in _quantities)
+ {
+ var projectGuid = HashGuid.ToHashGuid(quantity.Name).ToString("B").ToUpperInvariant();
+ Writer.WL($@"
+Project(""{_globalGuid}"") = ""{quantity.Name}"", ""UnitsNet.Modular\GeneratedCode\{quantity.Name}\{quantity.Name}.csproj"", ""{projectGuid}""
+EndProject");
+ sb.Append($"{projectGuid}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n");
+ sb.Append($"{projectGuid}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n");
+ sb.Append($"{projectGuid}.Debug|Any CPU.Deploy.0 = Debug|Any CPU\r\n");
+ sb.Append($"{projectGuid}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n");
+ sb.Append($"{projectGuid}.Release|Any CPU.Build.0 = Release|Any CPU\r\n");
+ sb.Append($"{projectGuid}.Release|Any CPU.Deploy.0 = Release|Any CPU\r\n");
+ }
+
+ Writer.WL($@"
+Project(""{_globalGuid}"") = ""UnitsNet.Core"", ""UnitsNet.Core\UnitsNet.Core.csproj"", ""{HashGuid.ToHashGuid("UnitsNet.Core").ToString("B").ToUpperInvariant()}""
+EndProject");
+
+ Writer.WL(@"Global
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution");
+
+ Writer.WL(sb.ToString());
+
+ Writer.WL($@"
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {_solutionGuid}
+ EndGlobalSection
+EndGlobal
+");
+ return Writer.ToString();
+ }
+ }
+}
diff --git a/CodeGen/Generators/UnitsNetModularGen/UnitTypeGenerator.cs b/CodeGen/Generators/UnitsNetModularGen/UnitTypeGenerator.cs
new file mode 100644
index 0000000000..e9b54529c3
--- /dev/null
+++ b/CodeGen/Generators/UnitsNetModularGen/UnitTypeGenerator.cs
@@ -0,0 +1,66 @@
+using CodeGen.Helpers;
+using CodeGen.Helpers.UnitEnumValueAllocation;
+using CodeGen.JsonTypes;
+
+namespace CodeGen.Generators.UnitsNetModularGen
+{
+ internal class UnitTypeGenerator : GeneratorBase
+ {
+ private readonly Quantity _quantity;
+ private readonly UnitEnumNameToValue _unitEnumNameToValue;
+ private readonly string _unitEnumName;
+
+ public UnitTypeGenerator(Quantity quantity, UnitEnumNameToValue unitEnumNameToValue)
+ {
+ _quantity = quantity;
+ _unitEnumNameToValue = unitEnumNameToValue;
+ _unitEnumName = $"{quantity.Name}Unit";
+ }
+
+ public string Generate()
+ {
+ Writer.WL(GeneratedFileHeader);
+ Writer.WL($@"
+// ReSharper disable once CheckNamespace
+namespace UnitsNet.Units
+{{
+ // Disable missing XML comment warnings for the generated unit enums.
+ #pragma warning disable 1591
+
+ public enum {_unitEnumName}
+ {{");
+ foreach (var unit in _quantity.Units)
+ {
+ if (unit.XmlDocSummary.HasText())
+ {
+ Writer.WL();
+ Writer.WL($@"
+ ///
+ /// {unit.XmlDocSummary}
+ /// ");
+ }
+
+ if (unit.XmlDocRemarks.HasText())
+ {
+ Writer.WL($@"
+ /// {unit.XmlDocRemarks}");
+ }
+
+ Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit.ObsoleteText));
+ Writer.WL($@"
+ {unit.SingularName} = {_unitEnumNameToValue[unit.SingularName]},");
+ }
+
+ Writer.WL($@"
+ }}
+
+ #pragma warning restore 1591
+}}");
+ return Writer.ToString();
+ }
+
+ private static string? GetObsoleteAttributeOrNull(string? obsoleteText) => string.IsNullOrWhiteSpace(obsoleteText)
+ ? null
+ : $"[System.Obsolete(\"{obsoleteText}\")]";
+ }
+}
diff --git a/CodeGen/Generators/UnitsNetModularGenerator.cs b/CodeGen/Generators/UnitsNetModularGenerator.cs
new file mode 100644
index 0000000000..04026ac25e
--- /dev/null
+++ b/CodeGen/Generators/UnitsNetModularGenerator.cs
@@ -0,0 +1,150 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System.IO;
+using System.Linq;
+using CodeGen.Generators.UnitsNetModularGen;
+using CodeGen.Helpers.UnitEnumValueAllocation;
+using CodeGen.JsonTypes;
+using Serilog;
+
+namespace CodeGen.Generators
+{
+ ///
+ /// Code generator for UnitsNet and UnitsNet.Tests projects.
+ ///
+ internal static class UnitsNetModularGenerator
+ {
+ ///
+ /// Generate source code for UnitsNet project for the given parsed quantities.
+ /// Outputs files relative to the given root dir to these locations:
+ ///
+ /// -
+ /// UnitsNet/GeneratedCode (quantity and unit types, Quantity, UnitAbbreviationCache)
+ ///
+ /// -
+ /// UnitsNet.Tests/GeneratedCode (tests)
+ ///
+ /// -
+ /// UnitsNet.Tests/CustomCode (test stubs, one for each quantity if not already created)
+ ///
+ ///
+ ///
+ /// Path to repository root directory.
+ /// The parsed quantities.
+ /// Allocated unit enum values for generating unit enum types.
+ public static void Generate(string rootDir, Quantity[] quantities, QuantityNameToUnitEnumValues quantityNameToUnitEnumValues)
+ {
+ var outputDir = $"{rootDir}/UnitsNet.Modular/GeneratedCode";
+ var testProjectDir = $"{rootDir}/UnitsNet.Modular.Tests";
+
+ // Ensure output directories exist
+ Directory.CreateDirectory($"{outputDir}/Quantities");
+ Directory.CreateDirectory($"{outputDir}/Units");
+ Directory.CreateDirectory($"{testProjectDir}/GeneratedCode");
+ Directory.CreateDirectory($"{testProjectDir}/GeneratedCode/TestsBase");
+ Directory.CreateDirectory($"{testProjectDir}/GeneratedCode/QuantityTests");
+
+ foreach (var quantity in quantities)
+ {
+ var projectPath = Path.Combine(outputDir, quantity.Name);
+ Directory.CreateDirectory(projectPath);
+
+ UnitEnumNameToValue unitEnumValues = quantityNameToUnitEnumValues[quantity.Name];
+
+ GenerateQuantity(quantity, Path.Combine(projectPath, $"{quantity.Name}.g.cs"));
+ GenerateUnitType(quantity, Path.Combine(projectPath, $"{quantity.Name}Unit.g.cs"), unitEnumValues);
+ GenerateProject(quantity, Path.Combine(projectPath, $"{quantity.Name}.csproj"));
+
+ // Example: CustomCode/Quantities/LengthTests inherits GeneratedCode/TestsBase/LengthTestsBase
+ // This way when new units are added to the quantity JSON definition, we auto-generate the new
+ // conversion function tests that needs to be manually implemented by the developer to fix the compile error
+ // so it cannot be forgotten.
+ // TODO modular
+ // GenerateQuantityTestBaseClass(quantity, $"{testProjectDir}/GeneratedCode/TestsBase/{quantity.Name}TestsBase.g.cs");
+ // GenerateQuantityTestClassIfNotExists(quantity, $"{testProjectDir}/CustomCode/{quantity.Name}Tests.cs");
+
+ Log.Information("✅ {Quantity}", quantity.Name);
+ }
+
+ Log.Information("");
+ // TODO modular
+ // GenerateIQuantityTests(quantities, $"{testProjectDir}/GeneratedCode/IQuantityTests.g.cs");
+ // GenerateStaticQuantity(quantities, $"{outputDir}/Quantity.g.cs");
+ GenerateSolution(quantities, rootDir);
+
+ var unitCount = quantities.SelectMany(q => q.Units).Count();
+ Log.Information("");
+ Log.Information("Total of {UnitCount} units and {QuantityCount} quantities", unitCount, quantities.Length);
+ Log.Information("");
+ }
+
+ private static void GenerateSolution(Quantity[] quantities, string outputDir)
+ {
+ var content = new SolutionGenerator(quantities).Generate();
+ var filePath = Path.Combine(outputDir, "UnitsNet.Modular.sln");
+
+ File.WriteAllText(filePath, content);
+ Log.Information("✅ UnitsNet.Modular.sln");
+ }
+
+ // private static void GenerateQuantityTestClassIfNotExists(Quantity quantity, string filePath)
+ // {
+ // if (File.Exists(filePath)) return;
+ //
+ // var content = new UnitTestStubGenerator(quantity).Generate();
+ // File.WriteAllText(filePath, content);
+ // Log.Information("✅ {Quantity} initial test stub", quantity.Name);
+ // }
+
+ private static void GenerateQuantity(Quantity quantity, string filePath)
+ {
+ var content = new QuantityGenerator(quantity).Generate();
+ File.WriteAllText(filePath, content);
+ }
+
+ // private static void GenerateNumberToExtensions(Quantity quantity, string filePath)
+ // {
+ // var content = new NumberExtensionsGenerator(quantity).Generate();
+ // File.WriteAllText(filePath, content);
+ // }
+
+ // private static void GenerateNumberToExtensionsTestClass(Quantity quantity, string filePath)
+ // {
+ // var content = new NumberExtensionsTestClassGenerator(quantity).Generate();
+ // File.WriteAllText(filePath, content);
+ // }
+
+ private static void GenerateUnitType(Quantity quantity, string filePath, UnitEnumNameToValue unitEnumValues)
+ {
+ var content = new UnitTypeGenerator(quantity, unitEnumValues).Generate();
+ File.WriteAllText(filePath, content);
+ }
+
+ private static void GenerateProject(Quantity quantity, string filePath)
+ {
+ var content = new ProjectGenerator(quantity).Generate();
+ File.WriteAllText(filePath, content);
+ }
+
+ // private static void GenerateQuantityTestBaseClass(Quantity quantity, string filePath)
+ // {
+ // var content = new UnitTestBaseClassGenerator(quantity).Generate();
+ // File.WriteAllText(filePath, content);
+ // }
+ //
+ // private static void GenerateIQuantityTests(Quantity[] quantities, string filePath)
+ // {
+ // var content = new IQuantityTestClassGenerator(quantities).Generate();
+ // File.WriteAllText(filePath, content);
+ // Log.Information("✅ IQuantityTests.g.cs");
+ // }
+
+ // private static void GenerateStaticQuantity(Quantity[] quantities, string filePath)
+ // {
+ // var content = new StaticQuantityGenerator(quantities).Generate();
+ // File.WriteAllText(filePath, content);
+ // Log.Information("✅ Quantity.g.cs");
+ // }
+ }
+}
diff --git a/CodeGen/Program.cs b/CodeGen/Program.cs
index d5589d5d1d..5d8d1dc0b4 100644
--- a/CodeGen/Program.cs
+++ b/CodeGen/Program.cs
@@ -70,6 +70,7 @@ public static int Main(bool verbose = false, DirectoryInfo? repositoryRoot = nul
QuantityNameToUnitEnumValues quantityNameToUnitEnumValues = UnitEnumValueAllocator.AllocateNewUnitEnumValues($"{rootDir}/Common/UnitEnumValues.g.json", quantities);
UnitsNetGenerator.Generate(rootDir, quantities, quantityNameToUnitEnumValues);
+ UnitsNetModularGenerator.Generate(rootDir, quantities, quantityNameToUnitEnumValues);
if (updateNanoFrameworkDependencies)
{
diff --git a/README.md b/README.md
index 3899257d92..3bf4e5ab6c 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,12 @@
[](https://codecov.io/gh/angularsen/UnitsNet)
[](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
+## Feature branch agl/split-nuget-v5
+
+### Challenges
+- UnitSystem and BaseUnits depending on 7 projects for SI units, change to string? Move to SI project and extension methods?
+-
+
## Units.NET
Add strongly typed quantities to your code and get merrily on with your life.
diff --git a/UnitsNet.Core.Tests/Class1.cs b/UnitsNet.Core.Tests/Class1.cs
new file mode 100644
index 0000000000..89c18369a7
--- /dev/null
+++ b/UnitsNet.Core.Tests/Class1.cs
@@ -0,0 +1,13 @@
+using FluentAssertions;
+using Xunit;
+
+namespace UnitsNet.Core.Tests;
+
+public class Class1
+{
+ [Fact]
+ public void Foo()
+ {
+ Quantity.Infos.Should().ContainSingle().Which.Name.Should().Be("HowMuch");
+ }
+}
diff --git a/UnitsNet.Core.Tests/CustomQuantities/HowMuch.cs b/UnitsNet.Core.Tests/CustomQuantities/HowMuch.cs
new file mode 100644
index 0000000000..f842c1d0c2
--- /dev/null
+++ b/UnitsNet.Core.Tests/CustomQuantities/HowMuch.cs
@@ -0,0 +1,62 @@
+namespace UnitsNet.Core.Tests.CustomQuantities
+{
+ ///
+ ///
+ /// Example of a custom/third-party quantity implementation, for plugging in quantities and units at runtime.
+ ///
+ public readonly struct HowMuch : IQuantity
+ {
+ public HowMuch(double value, HowMuchUnit unit)
+ {
+ Unit = unit;
+ Value = value;
+ }
+
+ Enum IQuantity.Unit => Unit;
+ public HowMuchUnit Unit { get; }
+
+ public QuantityValue Value { get; }
+
+ #region IQuantity
+
+ private static readonly HowMuch Zero = new HowMuch(0, HowMuchUnit.Some);
+
+ public BaseDimensions Dimensions => BaseDimensions.Dimensionless;
+
+ public static QuantityInfo Info => new(
+ nameof(HowMuch),
+ typeof(HowMuchUnit),
+ new UnitInfo[]
+ {
+ new UnitInfo(HowMuchUnit.Some, "Some", new[] { "sm", "some" }, BaseUnits.Undefined),
+ new UnitInfo(HowMuchUnit.ATon, "Tons", new[] { "tns", "tons" }, BaseUnits.Undefined),
+ new UnitInfo(HowMuchUnit.AShitTon, "ShitTons", new[] { "st", "shitn" }, BaseUnits.Undefined),
+ },
+ HowMuchUnit.Some,
+ Zero,
+ BaseDimensions.Dimensionless,
+ From);
+
+ public QuantityInfo QuantityInfo => Info;
+
+ public static IQuantity From(QuantityValue value, Enum unit) => new HowMuch((double)value, (HowMuchUnit)unit);
+
+ public double As(Enum unit) => Convert.ToDouble(unit);
+
+ // public double As(UnitSystem unitSystem) => throw new NotImplementedException();
+
+ public IQuantity ToUnit(Enum unit)
+ {
+ if (unit is HowMuchUnit howMuchUnit) return new HowMuch(As(unit), howMuchUnit);
+ throw new ArgumentException("Must be of type HowMuchUnit.", nameof(unit));
+ }
+
+ // public IQuantity ToUnit(UnitSystem unitSystem) => throw new NotImplementedException();
+
+ public override string ToString() => $"{Value} {Unit}";
+ public string ToString(string? format, IFormatProvider? formatProvider) => $"HowMuch ({format}, {formatProvider})";
+ public string ToString(IFormatProvider? provider) => $"HowMuch ({provider})";
+
+ #endregion
+ }
+}
diff --git a/UnitsNet.Core.Tests/CustomQuantities/HowMuchUnit.cs b/UnitsNet.Core.Tests/CustomQuantities/HowMuchUnit.cs
new file mode 100644
index 0000000000..06eca78649
--- /dev/null
+++ b/UnitsNet.Core.Tests/CustomQuantities/HowMuchUnit.cs
@@ -0,0 +1,12 @@
+namespace UnitsNet.Core.Tests.CustomQuantities
+{
+ ///
+ /// Example of a custom/third-party quantity implementation, for plugging in quantities and units at runtime.
+ ///
+ public enum HowMuchUnit
+ {
+ Some,
+ ATon,
+ AShitTon
+ }
+}
diff --git a/UnitsNet.Core.Tests/UnitsNet.Core.Tests.csproj b/UnitsNet.Core.Tests/UnitsNet.Core.Tests.csproj
new file mode 100644
index 0000000000..249b811e8e
--- /dev/null
+++ b/UnitsNet.Core.Tests/UnitsNet.Core.Tests.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
diff --git a/UnitsNet.Core/AmbiguousUnitParseException.cs b/UnitsNet.Core/AmbiguousUnitParseException.cs
new file mode 100644
index 0000000000..822c3c783d
--- /dev/null
+++ b/UnitsNet.Core/AmbiguousUnitParseException.cs
@@ -0,0 +1,28 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+
+namespace UnitsNet
+{
+ ///
+ /// Unable to parse because more than one unit of the given quantity type had this exact unit abbreviation.
+ /// Example: Length.Parse("1 pt") will throw , because both
+ /// and
+ /// have "pt" as their abbreviation.
+ ///
+ public class AmbiguousUnitParseException : UnitsNetException
+ {
+ ///
+ public AmbiguousUnitParseException(string message) : base(message)
+ {
+ HResult = 2;
+ }
+
+ ///
+ public AmbiguousUnitParseException(string message, Exception innerException) : base(message, innerException)
+ {
+ HResult = 2;
+ }
+ }
+}
diff --git a/UnitsNet.Core/AssemblyInfo.cs b/UnitsNet.Core/AssemblyInfo.cs
new file mode 100644
index 0000000000..d897374117
--- /dev/null
+++ b/UnitsNet.Core/AssemblyInfo.cs
@@ -0,0 +1,9 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Runtime.CompilerServices;
+
+[assembly: CLSCompliant(true)]
+[assembly: InternalsVisibleTo("UnitsNet.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010089abdcb0025f7d1c4c766686dd852b978ca5bb9fd80bba9d3539e8399b01170ae0ea10c0c3baa301b1d13090d5aff770532de00c88b67c4b24669fde7f9d87218f1c6c073a09016cbb2f87119b94227c2301f4e2a096043e30f7c47c872bbd8e0b80d924952e6b36990f13f847e83e9efb107ec2121fe39d7edaaa4e235af8c4")]
+
diff --git a/UnitsNet.Core/BaseDimensions.cs b/UnitsNet.Core/BaseDimensions.cs
new file mode 100644
index 0000000000..593625fd2e
--- /dev/null
+++ b/UnitsNet.Core/BaseDimensions.cs
@@ -0,0 +1,236 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Text;
+using System.Linq;
+
+namespace UnitsNet
+{
+ ///
+ /// Represents the base dimensions of a quantity.
+ ///
+ public sealed class BaseDimensions
+ {
+ /// Creates an instance of .
+ public BaseDimensions(int length, int mass, int time, int current, int temperature, int amount, int luminousIntensity)
+ {
+ Length = length;
+ Mass = mass;
+ Time = time;
+ Current = current;
+ Temperature = temperature;
+ Amount = amount;
+ LuminousIntensity = luminousIntensity;
+ }
+
+ ///
+ /// Checks if the dimensions represent a base quantity.
+ ///
+ /// True if the dimensions represent a base quantity, otherwise false.
+ public bool IsBaseQuantity()
+ {
+ var dimensionsArray = new[] { Length, Mass, Time, Current, Temperature, Amount, LuminousIntensity };
+ bool onlyOneEqualsOne = dimensionsArray.Where(dimension => dimension == 1).Take(2).Count() == 1;
+ return onlyOneEqualsOne;
+ }
+
+ ///
+ /// Checks if the dimensions represent a derived quantity.
+ ///
+ /// True if the dimensions represent a derived quantity, otherwise false.
+ public bool IsDerivedQuantity()
+ {
+ return !IsBaseQuantity() && !IsDimensionless();
+ }
+
+ ///
+ /// Checks if this base dimensions object represents a dimensionless quantity.
+ ///
+ /// True if this object represents a dimensionless quantity, otherwise false.
+ public bool IsDimensionless()
+ {
+ return this == Dimensionless;
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (obj is not BaseDimensions other)
+ return false;
+
+ return Length == other.Length &&
+ Mass == other.Mass &&
+ Time == other.Time &&
+ Current == other.Current &&
+ Temperature == other.Temperature &&
+ Amount == other.Amount &&
+ LuminousIntensity == other.LuminousIntensity;
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return new {Length, Mass, Time, Current, Temperature, Amount, LuminousIntensity}.GetHashCode();
+ }
+
+ ///
+ /// Get resulting dimensions after multiplying two dimensions, by performing addition of each dimension.
+ ///
+ /// Other dimensions.
+ /// Resulting dimensions.
+ public BaseDimensions Multiply(BaseDimensions right)
+ {
+ if(right is null)
+ throw new ArgumentNullException(nameof(right));
+
+ return new BaseDimensions(
+ Length + right.Length,
+ Mass + right.Mass,
+ Time + right.Time,
+ Current + right.Current,
+ Temperature + right.Temperature,
+ Amount + right.Amount,
+ LuminousIntensity + right.LuminousIntensity);
+ }
+
+ ///
+ /// Get resulting dimensions after dividing two dimensions, by performing subtraction of each dimension.
+ ///
+ /// Other dimensions.
+ /// Resulting dimensions.
+ public BaseDimensions Divide(BaseDimensions right)
+ {
+ if(right is null)
+ throw new ArgumentNullException(nameof(right));
+
+ return new BaseDimensions(
+ Length - right.Length,
+ Mass - right.Mass,
+ Time - right.Time,
+ Current - right.Current,
+ Temperature - right.Temperature,
+ Amount - right.Amount,
+ LuminousIntensity - right.LuminousIntensity);
+ }
+
+ ///
+ /// Check if two dimensions are equal.
+ ///
+ /// Left.
+ /// Right.
+ /// True if equal.
+ public static bool operator ==(BaseDimensions? left, BaseDimensions? right)
+ {
+ return left?.Equals(right!) ?? right is null;
+ }
+
+ ///
+ /// Check if two dimensions are unequal.
+ ///
+ /// Left.
+ /// Right.
+ /// True if not equal.
+ public static bool operator !=(BaseDimensions? left, BaseDimensions? right)
+ {
+ return !(left == right);
+ }
+
+ ///
+ /// Multiply two dimensions.
+ ///
+ /// Left.
+ /// Right.
+ /// Resulting dimensions.
+ public static BaseDimensions operator *(BaseDimensions left, BaseDimensions right)
+ {
+ if (left is null) throw new ArgumentNullException(nameof(left));
+ if (right is null) throw new ArgumentNullException(nameof(right));
+
+ return left.Multiply(right);
+ }
+
+ ///
+ /// Divide two dimensions.
+ ///
+ /// Left.
+ /// Right.
+ /// Resulting dimensions.
+ public static BaseDimensions operator /(BaseDimensions left, BaseDimensions right)
+ {
+ if (left is null) throw new ArgumentNullException(nameof(left));
+ if (right is null) throw new ArgumentNullException(nameof(right));
+
+ return left.Divide(right);
+ }
+
+ ///
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ AppendDimensionString(sb, "Length", Length);
+ AppendDimensionString(sb, "Mass", Mass);
+ AppendDimensionString(sb, "Time", Time);
+ AppendDimensionString(sb, "Current", Current);
+ AppendDimensionString(sb, "Temperature", Temperature);
+ AppendDimensionString(sb, "Amount", Amount);
+ AppendDimensionString(sb, "LuminousIntensity", LuminousIntensity);
+
+ return sb.ToString();
+ }
+
+ private static void AppendDimensionString(StringBuilder sb, string name, int value)
+ {
+ var absoluteValue = Math.Abs(value);
+
+ if(absoluteValue > 0)
+ {
+ sb.AppendFormat("[{0}]", name);
+
+ if(absoluteValue > 1)
+ sb.AppendFormat("^{0}", value);
+ }
+ }
+
+ ///
+ /// Gets the length dimensions (L).
+ ///
+ public int Length { get; }
+
+ ///
+ /// Gets the mass dimensions (M).
+ ///
+ public int Mass{ get; }
+
+ ///
+ /// Gets the time dimensions (T).
+ ///
+ public int Time{ get; }
+
+ ///
+ /// Gets the electric current dimensions (I).
+ ///
+ public int Current{ get; }
+
+ ///
+ /// Gets the temperature dimensions (Θ).
+ ///
+ public int Temperature{ get; }
+
+ ///
+ /// Gets the amount of substance dimensions (N).
+ ///
+ public int Amount{ get; }
+
+ ///
+ /// Gets the luminous intensity dimensions (J).
+ ///
+ public int LuminousIntensity{ get; }
+
+ ///
+ /// Represents a dimensionless (unitless) quantity.
+ ///
+ public static BaseDimensions Dimensionless { get; } = new BaseDimensions(0, 0, 0, 0, 0, 0, 0);
+ }
+}
diff --git a/UnitsNet.Core/BaseUnits.cs b/UnitsNet.Core/BaseUnits.cs
new file mode 100644
index 0000000000..529d43660e
--- /dev/null
+++ b/UnitsNet.Core/BaseUnits.cs
@@ -0,0 +1,206 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Text;
+
+namespace UnitsNet
+{
+ ///
+ ///
+ /// Represents the base units for a quantity. All quantities, both base and derived, can be
+ /// represented by a combination of these seven base units.
+ ///
+ public sealed class BaseUnits : IEquatable
+ {
+ ///
+ /// Represents BaseUnits that have not been defined.
+ ///
+ public static BaseUnits Undefined { get; } = new BaseUnits();
+
+ ///
+ /// Creates an instance of if the base units class that represents the base units for a quantity.
+ /// All quantities, both base and derived, can be represented by a combination of these seven base units.
+ ///
+ /// The length unit (L).
+ /// The mass unit (M).
+ /// The time unit (T).
+ /// The electric current unit (I).
+ /// The temperature unit (Θ).
+ /// The amount of substance unit (N).
+ /// The luminous intensity unit (J).
+ public BaseUnits(
+ Enum? length = null,
+ Enum? mass = null,
+ Enum? time = null,
+ Enum? current = null,
+ Enum? temperature = null,
+ Enum? amount = null,
+ Enum? luminousIntensity = null)
+ {
+ Length = length;
+ Mass = mass;
+ Time = time;
+ Current = current;
+ Temperature = temperature;
+ Amount = amount;
+ LuminousIntensity = luminousIntensity;
+
+ IsFullyDefined = Length is not null &&
+ Mass is not null &
+ Time is not null &&
+ Current is not null &&
+ Temperature is not null &&
+ Amount is not null &&
+ LuminousIntensity is not null;
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ return obj is BaseUnits other && Equals(other);
+ }
+
+ ///
+ /// Checks if all of the base units are equal to another instance's.
+ ///
+ /// The other instance to check if equal to.
+ /// True if equal, otherwise false.
+ public bool Equals(BaseUnits? other)
+ {
+ if (other is null)
+ return false;
+
+ return Length == other.Length &&
+ Mass == other.Mass &&
+ Time == other.Time &&
+ Current == other.Current &&
+ Temperature == other.Temperature &&
+ Amount == other.Amount &&
+ LuminousIntensity == other.LuminousIntensity;
+ }
+
+ ///
+ /// Checks if the base units are a subset of another. Undefined base units are ignored.
+ /// If all base united are undefined (equal to ),
+ /// IsSubsetOf will return true only if other is also equal to .
+ ///
+ /// The other to compare to.
+ /// True if the base units are a subset of other, otherwise false.
+ public bool IsSubsetOf(BaseUnits other)
+ {
+ if (other == null) throw new ArgumentNullException(nameof(other));
+
+ // If all base units are undefined, can only be a subset of another where all base units are undefined.
+ if (Equals(Undefined))
+ return other.Equals(Undefined);
+
+ return (Length == null || Length == other.Length) &&
+ (Mass == null || Mass == other.Mass) &&
+ (Time == null || Time == other.Time) &&
+ (Current == null || Current == other.Current) &&
+ (Temperature == null || Temperature == other.Temperature) &&
+ (Amount == null || Amount == other.Amount) &&
+ (LuminousIntensity == null || LuminousIntensity == other.LuminousIntensity);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return new
+ {
+ Length,
+ Mass,
+ Time,
+ Current,
+ Temperature,
+ Amount,
+ LuminousIntensity
+ }.GetHashCode();
+ }
+
+ ///
+ /// Checks if this instance is equal to another.
+ ///
+ /// The left instance.
+ /// The right instance.
+ /// True if equal, otherwise false.
+ ///
+ public static bool operator ==(BaseUnits? left, BaseUnits? right)
+ {
+ return left?.Equals(right!) ?? right is null;
+ }
+
+ ///
+ /// Checks if this instance is not equal to another.
+ ///
+ /// The left instance.
+ /// The right instance.
+ /// True if not equal, otherwise false.
+ ///
+ public static bool operator !=(BaseUnits? left, BaseUnits? right)
+ {
+ return !(left == right);
+ }
+
+ ///
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ string GetDefaultAbbreviation(Enum? unitOrNull) => unitOrNull is { } unit
+ ? UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit)
+ : "N/A";
+
+ sb.AppendFormat("[Length]: {0}, ", GetDefaultAbbreviation(Length));
+ sb.AppendFormat("[Mass]: {0}, ", GetDefaultAbbreviation(Mass));
+ sb.AppendFormat("[Time]: {0}, ", GetDefaultAbbreviation(Time));
+ sb.AppendFormat("[Current]: {0}, ", GetDefaultAbbreviation(Current));
+ sb.AppendFormat("[Temperature]: {0}, ", GetDefaultAbbreviation(Temperature));
+ sb.AppendFormat("[Amount]: {0}, ", GetDefaultAbbreviation(Amount));
+ sb.AppendFormat("[LuminousIntensity]: {0}", GetDefaultAbbreviation(LuminousIntensity));
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Gets the length unit (L).
+ ///
+ public Enum? Length { get; }
+
+ ///
+ /// Gets the mass unit (M).
+ ///
+ public Enum? Mass { get; }
+
+ ///
+ /// Gets the time unit (T).
+ ///
+ public Enum? Time { get; }
+
+ ///
+ /// Gets the electric current unit (I).
+ ///
+ public Enum? Current { get; }
+
+ ///
+ /// Gets the temperature unit (Θ).
+ ///
+ public Enum? Temperature { get; }
+
+ ///
+ /// Gets the amount of substance unit (N).
+ ///
+ public Enum? Amount { get; }
+
+ ///
+ /// Gets the luminous intensity unit (J).
+ ///
+ public Enum? LuminousIntensity { get; }
+
+ ///
+ /// Gets whether or not all of the base units are defined.
+ ///
+ public bool IsFullyDefined { get; }
+ }
+}
diff --git a/UnitsNet.Core/Comparison.cs b/UnitsNet.Core/Comparison.cs
new file mode 100644
index 0000000000..ac6726c3e2
--- /dev/null
+++ b/UnitsNet.Core/Comparison.cs
@@ -0,0 +1,237 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+
+namespace UnitsNet
+{
+ ///
+ /// Helper methods to perform relative and absolute comparison.
+ ///
+ public static class Comparison
+ {
+ ///
+ ///
+ /// Checks if two values are equal with a given relative or absolute tolerance.
+ ///
+ ///
+ /// Relative tolerance is defined as the maximum allowable absolute difference between
+ /// and
+ /// as a percentage of . A relative tolerance of
+ /// 0.01 means the
+ /// absolute difference of and must be within +/-
+ /// 1%.
+ ///
+ /// In this example, the two values will be equal if the value of b is within +/- 1% of a.
+ ///
+ /// Equals(a, b, 0.01, ComparisonType.Relative);
+ ///
+ ///
+ ///
+ ///
+ /// Absolute tolerance is defined as the maximum allowable absolute difference between
+ /// and
+ /// as a fixed number.
+ ///
+ /// In this example, the two values will be equal if abs( -
+ /// ) <= 0.01
+ ///
+ /// Equals(a, b, 0.01, ComparisonType.Absolute);
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The reference value. If using relative tolerance, it is the value which the relative
+ /// tolerance will be calculated against.
+ ///
+ /// The value to compare to.
+ /// The absolute or relative tolerance value. Must be greater than or equal to 0.
+ /// Whether the tolerance is absolute or relative.
+ ///
+ public static bool Equals(double referenceValue, double otherValue, double tolerance, ComparisonType comparisonType)
+ {
+ if (tolerance < 0)
+ throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be greater than or equal to 0");
+
+ switch (comparisonType)
+ {
+ case ComparisonType.Relative:
+ return EqualsRelative(referenceValue, otherValue, tolerance);
+ case ComparisonType.Absolute:
+ return EqualsAbsolute(referenceValue, otherValue, tolerance);
+ default:
+ throw new InvalidOperationException("The given ComparisonType is not supported.");
+ }
+ }
+
+ ///
+ ///
+ /// Checks if two values are equal with a given relative or absolute tolerance.
+ ///
+ ///
+ /// Relative tolerance is defined as the maximum allowable absolute difference between
+ /// and
+ /// as a percentage of . A relative tolerance of
+ /// 0.01 means the
+ /// absolute difference of and must be within +/-
+ /// 1%.
+ ///
+ /// In this example, the two values will be equal if the value of b is within +/- 1% of a.
+ ///
+ /// Equals(a, b, 0.01, ComparisonType.Relative);
+ ///
+ ///
+ ///
+ ///
+ /// Absolute tolerance is defined as the maximum allowable absolute difference between
+ /// and
+ /// as a fixed number.
+ ///
+ /// In this example, the two values will be equal if abs( -
+ /// ) <= 0.01
+ ///
+ /// Equals(a, b, 0.01, ComparisonType.Absolute);
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The reference value. If using relative tolerance, it is the value which the relative
+ /// tolerance will be calculated against.
+ ///
+ /// The value to compare to.
+ /// The absolute or relative tolerance value. Must be greater than or equal to 0.
+ /// Whether the tolerance is absolute or relative.
+ ///
+ public static bool Equals(decimal referenceValue, decimal otherValue, decimal tolerance, ComparisonType comparisonType)
+ {
+ if (tolerance < 0)
+ throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be greater than or equal to 0");
+
+ switch (comparisonType)
+ {
+ case ComparisonType.Relative:
+ return EqualsRelative(referenceValue, otherValue, tolerance);
+ case ComparisonType.Absolute:
+ return EqualsAbsolute(referenceValue, otherValue, tolerance);
+ default:
+ throw new InvalidOperationException("The given ComparisonType is not supported.");
+ }
+ }
+
+ ///
+ /// Checks if two values are equal with a given relative tolerance.
+ ///
+ /// Relative tolerance is defined as the maximum allowable absolute difference between
+ /// and
+ /// as a percentage of . A relative tolerance of
+ /// 0.01 means the
+ /// absolute difference of and must be within +/-
+ /// 1%.
+ ///
+ /// In this example, the two values will be equal if the value of b is within +/- 1% of a.
+ ///
+ /// EqualsRelative(a, b, 0.01);
+ ///
+ ///
+ ///
+ ///
+ /// The reference value which the tolerance will be calculated against.
+ /// The value to compare to.
+ /// The relative tolerance. Must be greater than or equal to 0.
+ /// True if the two values are equal within the given relative tolerance, otherwise false.
+ public static bool EqualsRelative(double referenceValue, double otherValue, double tolerance)
+ {
+ if (tolerance < 0)
+ throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be greater than or equal to 0");
+
+ var maxVariation = Math.Abs(referenceValue * tolerance);
+ return Math.Abs(referenceValue - otherValue) <= maxVariation;
+ }
+
+ ///
+ /// Checks if two values are equal with a given relative tolerance.
+ ///
+ /// Relative tolerance is defined as the maximum allowable absolute difference between
+ /// and
+ /// as a percentage of . A relative tolerance of
+ /// 0.01 means the
+ /// absolute difference of and must be within +/-
+ /// 1%.
+ ///
+ /// In this example, the two values will be equal if the value of b is within +/- 1% of a.
+ ///
+ /// EqualsRelative(a, b, 0.01);
+ ///
+ ///
+ ///
+ ///
+ /// The reference value which the tolerance will be calculated against.
+ /// The value to compare to.
+ /// The relative tolerance. Must be greater than or equal to 0.
+ /// True if the two values are equal within the given relative tolerance, otherwise false.
+ public static bool EqualsRelative(decimal referenceValue, decimal otherValue, decimal tolerance)
+ {
+ if (tolerance < 0)
+ throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be greater than or equal to 0");
+
+ var maxVariation = Math.Abs(referenceValue * tolerance);
+ return Math.Abs(referenceValue - otherValue) <= maxVariation;
+ }
+
+ ///
+ /// Checks if two values are equal with a given absolute tolerance.
+ ///
+ /// Absolute tolerance is defined as the maximum allowable absolute difference between
+ /// and
+ /// as a fixed number.
+ ///
+ /// In this example, the two values will be equal if abs( -
+ /// ) <= 0.01
+ ///
+ /// Equals(a, b, 0.01, ComparisonType.Absolute);
+ ///
+ ///
+ ///
+ ///
+ /// The first value.
+ /// The second value.
+ /// The absolute tolerance. Must be greater than or equal to 0.
+ /// True if the two values are equal within the given absolute tolerance, otherwise false.
+ public static bool EqualsAbsolute(double value1, double value2, double tolerance)
+ {
+ if (tolerance < 0)
+ throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be greater than or equal to 0");
+
+ return Math.Abs(value1 - value2) <= tolerance;
+ }
+
+ ///
+ /// Checks if two values are equal with a given absolute tolerance.
+ ///
+ /// Absolute tolerance is defined as the maximum allowable absolute difference between
+ /// and
+ /// as a fixed number.
+ ///
+ /// In this example, the two values will be equal if abs( -
+ /// ) <= 0.01
+ ///
+ /// Equals(a, b, 0.01, ComparisonType.Absolute);
+ ///
+ ///
+ ///
+ ///
+ /// The first value.
+ /// The second value.
+ /// The absolute tolerance. Must be greater than or equal to 0.
+ /// True if the two values are equal within the given absolute tolerance, otherwise false.
+ public static bool EqualsAbsolute(decimal value1, decimal value2, decimal tolerance)
+ {
+ if (tolerance < 0)
+ throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be greater than or equal to 0");
+
+ return Math.Abs(value1 - value2) <= tolerance;
+ }
+ }
+}
diff --git a/UnitsNet.Core/ComparisonType.cs b/UnitsNet.Core/ComparisonType.cs
new file mode 100644
index 0000000000..18f76cfdd3
--- /dev/null
+++ b/UnitsNet.Core/ComparisonType.cs
@@ -0,0 +1,21 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+namespace UnitsNet
+{
+ ///
+ /// Specifies if the comparison between numbers is absolute or relative.
+ ///
+ public enum ComparisonType
+ {
+ ///
+ /// Error margin in relative size to a reference value.
+ ///
+ Relative,
+
+ ///
+ /// Error margin as absolute size.
+ ///
+ Absolute
+ }
+}
diff --git a/UnitsNet.Core/CompiledLambdas.cs b/UnitsNet.Core/CompiledLambdas.cs
new file mode 100644
index 0000000000..56790cadaa
--- /dev/null
+++ b/UnitsNet.Core/CompiledLambdas.cs
@@ -0,0 +1,312 @@
+using System;
+using System.Linq.Expressions;
+
+namespace UnitsNet
+{
+ ///
+ /// Compiled lambda expressions that can be invoked with generic run-time parameters. This is used for performance as
+ /// it is far faster than reflection based alternatives.
+ ///
+ internal static class CompiledLambdas
+ {
+ ///
+ /// Multiplies the given values.
+ ///
+ /// The type of the operation (left hand side, right hand side, and result).
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The multiplied result.
+ internal static T Multiply(T left, T right) => MultiplyImplementation.Invoke(left, right);
+
+ ///
+ /// Multiplies the given values.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The result type.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The multiplied result.
+ internal static TResult Multiply(TLeft left, TRight right) =>
+ MultiplyImplementation.Invoke(left, right);
+
+ ///
+ /// Divides the given values.
+ ///
+ /// The type of the operation (left hand side, right hand side, and result).
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The divided result.
+ internal static T Divide(T left, T right) => DivideImplementation.Invoke(left, right);
+
+ ///
+ /// Divides the given values.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The result type.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The divided result.
+ internal static TResult Divide(TLeft left, TRight right) =>
+ DivideImplementation.Invoke(left, right);
+
+ ///
+ /// Adds the given values.
+ ///
+ /// The type of the operation (left hand side, right hand side, and result).
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The added result.
+ internal static T Add(T left, T right) => AddImplementation.Invoke(left, right);
+
+ ///
+ /// Adds the given values.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The result type.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The added result.
+ internal static TResult Add(TLeft left, TRight right) =>
+ AddImplementation.Invoke(left, right);
+
+ ///
+ /// Subtracts the given values.
+ ///
+ /// The type of the operation (left hand side, right hand side, and result).
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The subtracted result.
+ internal static T Subtract(T left, T right) => SubtractImplementation.Invoke(left, right);
+
+ ///
+ /// Subtracts the given values.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The result type.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The subtracted result.
+ internal static TResult Subtract(TLeft left, TRight right) =>
+ SubtractImplementation.Invoke(left, right);
+
+ ///
+ /// Gets the modulus of the given values.
+ ///
+ /// The type of the operation (left hand side, right hand side, and result).
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The modulus.
+ internal static T Modulo(T left, T right) => ModuloImplementation.Invoke(left, right);
+
+ ///
+ /// Gets the modulus of the given values.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The result type.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// The modulus.
+ internal static TResult Modulo(TLeft left, TRight right) =>
+ ModuloImplementation.Invoke(left, right);
+
+ ///
+ /// Checks if the left and right hand side are equal.
+ ///
+ /// The type of both the left and right hand side.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// True if equal, otherwise false.
+ internal static bool Equal(T left, T right) => EqualImplementation.Invoke(left, right);
+
+ ///
+ /// Checks if the left and right hand side are equal.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// True if equal, otherwise false.
+ internal static bool Equal(TLeft left, TRight right) =>
+ EqualImplementation.Invoke(left, right);
+
+ ///
+ /// Checks if the left and right hand side are not equal.
+ ///
+ /// The type of both the left and right hand side.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// True if not equal, otherwise false.
+ internal static bool NotEqual(T left, T right) => NotEqualImplementation.Invoke(left, right);
+
+ ///
+ /// Checks if the left and right hand side are not equal.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// True if not equal, otherwise false.
+ internal static bool NotEqual(TLeft left, TRight right) =>
+ NotEqualImplementation.Invoke(left, right);
+
+ ///
+ /// Checks if the left hand side is less than the right hand side.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// True if the left hand side is less than the right hand side, otherwise false.
+ internal static bool LessThan(TLeft left, TRight right) =>
+ LessThanImplementation.Invoke(left, right);
+
+ ///
+ /// Checks if the left hand side is less than or equal to the right hand side.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// True if the left hand side is less than or equal to the right hand side, otherwise false.
+ internal static bool LessThanOrEqual(TLeft left, TRight right) =>
+ LessThanOrEqualImplementation.Invoke(left, right);
+
+ ///
+ /// Checks if the left hand side is greater than the right hand side.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// True if the left hand side is greater than the right hand side, otherwise false.
+ internal static bool GreaterThan(TLeft left, TRight right) =>
+ GreaterThanImplementation.Invoke(left, right);
+
+ ///
+ /// Checks if the left hand side is greater than or equal to the right hand side.
+ ///
+ /// The type of the left hand side.
+ /// The type of the right hand side.
+ /// The left hand side parameter.
+ /// The right hand side parameter.
+ /// True if the left hand side is greater than or equal to the right hand side, otherwise false.
+ internal static bool GreaterThanOrEqual(TLeft left, TRight right) =>
+ GreaterThanOrEqualImplementation.Invoke(left, right);
+
+ #region Implementation Classes
+
+ private static class MultiplyImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.Multiply);
+
+ internal static TResult Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class DivideImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.Divide);
+
+ internal static TResult Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class AddImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.Add);
+
+ internal static TResult Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class SubtractImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.Subtract);
+
+ internal static TResult Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class ModuloImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.Modulo);
+
+ internal static TResult Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class EqualImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.Equal);
+
+ internal static bool Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class NotEqualImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.NotEqual);
+
+ internal static bool Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class LessThanImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.LessThan);
+
+ internal static bool Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class LessThanOrEqualImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.LessThanOrEqual);
+
+ internal static bool Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class GreaterThanImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.GreaterThan);
+
+ internal static bool Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ private static class GreaterThanOrEqualImplementation
+ {
+ private readonly static Func Function =
+ CreateBinaryFunction(Expression.GreaterThanOrEqual);
+
+ internal static bool Invoke(TLeft left, TRight right) => Function(left, right);
+ }
+
+ #endregion
+
+ ///
+ /// Creates a compiled lambda for the given .
+ ///
+ /// The type of the left hand side of the binary operation.
+ /// The type of the right hand side of the binary operation.
+ /// The type of the result of the binary operation.
+ /// The function that creates a binary expression to compile.
+ /// The compiled binary expression.
+ private static Func CreateBinaryFunction(Func expressionCreationFunction)
+ {
+ var leftParameter = Expression.Parameter(typeof(TLeft), "left");
+ var rightParameter = Expression.Parameter(typeof(TRight), "right");
+
+ var binaryExpression = expressionCreationFunction(leftParameter, rightParameter);
+ var lambda = Expression.Lambda>(binaryExpression, leftParameter, rightParameter);
+
+ return lambda.Compile();
+ }
+ }
+}
diff --git a/UnitsNet.Core/CustomCode/Quantity.cs b/UnitsNet.Core/CustomCode/Quantity.cs
new file mode 100644
index 0000000000..14f119e1ed
--- /dev/null
+++ b/UnitsNet.Core/CustomCode/Quantity.cs
@@ -0,0 +1,177 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+
+namespace UnitsNet
+{
+ public partial class Quantity
+ {
+ private static readonly Lazy> UnitTypeAndNameToUnitInfoLazy;
+
+ static Quantity()
+ {
+ // Automatically load quantity types from UnitsNet libraries. Custom quantity types must be explicitly loaded.
+ // Example full name: "UnitsNet, Version=5.0.0.0, Culture=neutral, PublicKeyToken=f8601875a1f041da"
+ // Example full name: "UnitsNet.Modular.Length, Version=5.0.0.0, Culture=neutral, PublicKeyToken=f8601875a1f041da"
+ var unitsNetAssemblyRegex = new Regex(@"^UnitsNet(\.[^,]+)?\,");
+ var quantityTypes = AppDomain.CurrentDomain.GetAssemblies()
+ .Where(ass => ass.FullName != null && unitsNetAssemblyRegex.IsMatch(ass.FullName))
+ .SelectMany(ass => ass.DefinedTypes.Where(t => typeof(IQuantity).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract))
+ .ToList();
+
+ QuantityTypes = quantityTypes.AsReadOnly();
+
+ List quantityInfos = quantityTypes
+ .Select(t => t.GetProperty("Info", BindingFlags.Public|BindingFlags.Static)?.GetMethod?.Invoke(null, null) as QuantityInfo)
+ .Where(info => info != null)
+ .Select(info => info!)
+ .ToList();
+
+ Names = quantityInfos.Select(qt => qt.Name).ToArray();
+ EnumToQuantityInfo = new ReadOnlyDictionary(quantityInfos.ToDictionary(qt => qt.UnitType, qt => qt));
+
+ Infos = quantityInfos
+ .OrderBy(quantityInfo => quantityInfo.Name)
+ .ToArray();
+
+ UnitTypeAndNameToUnitInfoLazy = new Lazy>(() =>
+ {
+ return Infos
+ .SelectMany(quantityInfo => quantityInfo.UnitInfos
+ .Select(unitInfo => new KeyValuePair<(Type, string), UnitInfo>(
+ (unitInfo.Value.GetType(), unitInfo.Name),
+ unitInfo)))
+ .ToDictionary(x => x.Key, x => x.Value);
+ });
+ }
+
+ ///
+ /// Map unit enum type to its corresponding QuantityInfo, such as LengthUnit -> Length.Info.
+ ///
+ public static ReadOnlyDictionary EnumToQuantityInfo { get; }
+
+ ///
+ /// All quantity types found in loaded assemblies that implement .
+ ///
+ public static ReadOnlyCollection QuantityTypes { get; }
+
+ ///
+ /// All enum value names of , such as "Length" and "Mass".
+ ///
+ public static string[] Names { get; }
+
+ ///
+ /// All quantity information objects, such as and .
+ ///
+ public static IReadOnlyList Infos { get; }
+
+ ///
+ /// Get for a given unit enum value.
+ ///
+ public static UnitInfo GetUnitInfo(Enum unitEnum) => UnitTypeAndNameToUnitInfoLazy.Value[(unitEnum.GetType(), unitEnum.ToString())];
+
+ ///
+ /// Try to get for a given unit enum value.
+ ///
+ public static bool TryGetUnitInfo(Enum unitEnum, [NotNullWhen(true)] out UnitInfo? unitInfo) =>
+ UnitTypeAndNameToUnitInfoLazy.Value.TryGetValue((unitEnum.GetType(), unitEnum.ToString()), out unitInfo);
+
+ ///
+ /// Dynamically construct a quantity.
+ ///
+ /// Numeric value.
+ /// Unit enum value.
+ /// An object.
+ /// Unit value is not a know unit enum type.
+ public static IQuantity From(QuantityValue value, Enum unit)
+ {
+ if (!EnumToQuantityInfo.TryGetValue(unit.GetType(), out QuantityInfo? quantityInfo))
+ {
+ throw new ArgumentException($"Unit enum type '{unit.GetType()}' is not a known unit enum type. Make sure to expose a static QuantityInfo getter-property named 'Info' on the quantity class and to configure this unit enum type there.", nameof(unit));
+ }
+
+ return quantityInfo.CreateQuantity(value, unit);
+ }
+
+ ///
+ public static bool TryFrom(double value, Enum unit, [NotNullWhen(true)] out IQuantity? quantity)
+ {
+ // Implicit cast to QuantityValue would prevent TryFrom from being called,
+ // so we need to explicitly check this here for double arguments.
+ if (double.IsNaN(value) || double.IsInfinity(value))
+ {
+ quantity = default(IQuantity);
+ return false;
+ }
+
+ return TryFrom((QuantityValue)value, unit, out quantity);
+ }
+
+ ///
+ public static bool TryFrom(QuantityValue value, Enum unit, [NotNullWhen(true)] out IQuantity? quantity)
+ {
+ if (!EnumToQuantityInfo.TryGetValue(unit.GetType(), out QuantityInfo? quantityInfo))
+ {
+ quantity = null;
+ return false;
+ }
+
+ quantity = quantityInfo.CreateQuantity(value, unit);
+ return true;
+ }
+
+ ///
+ public static IQuantity Parse(Type quantityType, string quantityString) => Parse(null, quantityType, quantityString);
+
+ ///
+ /// Dynamically parse a quantity string representation.
+ ///
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Type of quantity, such as .
+ /// Quantity string representation, such as "1.5 kg". Must be compatible with given quantity type.
+ /// The parsed quantity.
+ /// Type must be of type UnitsNet.IQuantity -or- Type is not a known quantity type.
+ public static IQuantity Parse(IFormatProvider? formatProvider, Type quantityType, string quantityString)
+ {
+ if (!typeof(IQuantity).IsAssignableFrom(quantityType))
+ throw new ArgumentException($"Type {quantityType} must be of type UnitsNet.IQuantity.");
+
+ if (TryParse(formatProvider, quantityType, quantityString, out IQuantity? quantity)) return quantity;
+
+ throw new ArgumentException($"Quantity string could not be parsed to quantity {quantityType}.");
+ }
+
+ private static bool TryParse(IFormatProvider? formatProvider, Type quantityType, string quantityString, [NotNullWhen(true)] out IQuantity? quantity)
+ {
+ quantity = null;
+
+ if (!typeof(IQuantity).IsAssignableFrom(quantityType)) return false;
+
+ // TODO Create dictionary to optimize lookup.
+ if (Infos.FirstOrDefault(i => i.ValueType == quantityType) is not { } qi) return false;
+
+ return qi.TryParse(formatProvider, quantityString, out quantity);
+ }
+
+ ///
+ public static bool TryParse(Type quantityType, string quantityString, [NotNullWhen(true)] out IQuantity? quantity) =>
+ TryParse(null, quantityType, quantityString, out quantity);
+
+ ///
+ /// Get a list of quantities that has the given base dimensions.
+ ///
+ /// The base dimensions to match.
+ public static IEnumerable GetQuantitiesWithBaseDimensions(BaseDimensions baseDimensions)
+ {
+ return Infos.Where(info => info.BaseDimensions.Equals(baseDimensions));
+ }
+ }
+}
diff --git a/UnitsNet.Core/CustomCode/QuantityParser.cs b/UnitsNet.Core/CustomCode/QuantityParser.cs
new file mode 100644
index 0000000000..23a0b16881
--- /dev/null
+++ b/UnitsNet.Core/CustomCode/QuantityParser.cs
@@ -0,0 +1,257 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+// ReSharper disable once CheckNamespace
+namespace UnitsNet
+{
+ internal delegate TQuantity QuantityFromDelegate(QuantityValue value, TUnitType fromUnit)
+ where TQuantity : IQuantity
+ where TUnitType : Enum;
+
+ internal class QuantityParser
+ {
+ ///
+ /// Allow integer, floating point or exponential number formats.
+ ///
+ private const NumberStyles ParseNumberStyles = NumberStyles.Number | NumberStyles.Float | NumberStyles.AllowExponent;
+
+ private readonly UnitAbbreviationsCache _unitAbbreviationsCache;
+ private readonly UnitParser _unitParser;
+
+ public static QuantityParser Default { get; }
+
+ public QuantityParser(UnitAbbreviationsCache? unitAbbreviationsCache)
+ {
+ _unitAbbreviationsCache = unitAbbreviationsCache ?? UnitAbbreviationsCache.Default;
+ _unitParser = new UnitParser(_unitAbbreviationsCache);
+ }
+
+ static QuantityParser()
+ {
+ Default = new QuantityParser(UnitAbbreviationsCache.Default);
+ }
+
+ [SuppressMessage("ReSharper", "UseStringInterpolation")]
+ internal TQuantity Parse(string str,
+ IFormatProvider? formatProvider,
+ QuantityFromDelegate fromDelegate)
+ where TQuantity : IQuantity
+ where TUnitType : Enum
+ {
+ if (str == null) throw new ArgumentNullException(nameof(str));
+ str = str.Trim();
+
+ var regex = CreateRegexForQuantity(formatProvider);
+
+ if (!TryExtractValueAndUnit(regex, str, out var valueString, out var unitString))
+ {
+ var ex = new FormatException("Unable to parse quantity. Expected the form \"{value} {unit abbreviation}\", such as \"5.5 m\". The spacing is optional.");
+ ex.Data["input"] = str;
+ throw ex;
+ }
+
+ return ParseWithRegex(valueString, unitString, fromDelegate, formatProvider);
+ }
+
+ [SuppressMessage("ReSharper", "UseStringInterpolation")]
+ internal bool TryParse(string? str,
+ IFormatProvider? formatProvider,
+ Type unitType,
+ IEnumerable unitAbbreviations,
+ QuantityFactory quantityFactory,
+ out IQuantity? result)
+ {
+ result = default;
+
+ if (string.IsNullOrWhiteSpace(str)) return false;
+ str = str!.Trim(); // netstandard2.0 nullable quirk
+
+ Regex regex = CreateRegexForUnitAbbreviations(unitAbbreviations);
+
+ return TryExtractValueAndUnit(regex, str, out var valueString, out var unitString) &&
+ TryParseWithRegex(valueString, unitString, unitType, quantityFactory, formatProvider, out result);
+ }
+
+ [SuppressMessage("ReSharper", "UseStringInterpolation")]
+ internal bool TryParse(string? str,
+ IFormatProvider? formatProvider,
+ QuantityFromDelegate fromDelegate,
+ out TQuantity result)
+ where TQuantity : struct, IQuantity
+ where TUnitType : struct, Enum
+ {
+ result = default;
+
+ if (string.IsNullOrWhiteSpace(str)) return false;
+ str = str!.Trim(); // netstandard2.0 nullable quirk
+
+ var regex = CreateRegexForQuantity(formatProvider);
+
+ return TryExtractValueAndUnit(regex, str, out var valueString, out var unitString) &&
+ TryParseWithRegex(valueString, unitString, fromDelegate, formatProvider, out result);
+ }
+
+ ///
+ /// Workaround for C# not allowing to pass on 'out' param from type Length to IQuantity, even though the are compatible.
+ ///
+ [SuppressMessage("ReSharper", "UseStringInterpolation")]
+ internal bool TryParse(string str,
+ IFormatProvider? formatProvider,
+ QuantityFromDelegate fromDelegate,
+ out IQuantity? result)
+ where TQuantity : struct, IQuantity
+ where TUnitType : struct, Enum
+ {
+ if (TryParse(str, formatProvider, fromDelegate, out TQuantity parsedQuantity))
+ {
+ result = parsedQuantity;
+ return true;
+ }
+
+ result = default;
+ return false;
+ }
+
+ internal string CreateRegexPatternForUnit(
+ TUnitType unit,
+ IFormatProvider? formatProvider,
+ bool matchEntireString = true)
+ where TUnitType : Enum
+ {
+ var unitAbbreviations = _unitAbbreviationsCache.GetUnitAbbreviations(unit, formatProvider);
+ return GetRegexPatternForUnitAbbreviations(unitAbbreviations, matchEntireString);
+ }
+
+ private static string GetRegexPatternForUnitAbbreviations(IEnumerable unitAbbreviations, bool matchEntireString)
+ {
+ var pattern = GetRegexPatternForUnitAbbreviations(unitAbbreviations);
+ return matchEntireString ? $"^{pattern}$" : pattern;
+ }
+
+ private static string GetRegexPatternForUnitAbbreviations(IEnumerable abbreviations)
+ {
+ var orderedAbbreviations = abbreviations
+ .OrderByDescending(s => s.Length) // Important to order by length -- if "m" is before "mm" and the input is "mm", it will match just "m"
+ .Select(Regex.Escape) // Escape special regex characters
+ .ToArray();
+
+ var abbreviationsPiped = $"{string.Join("|", orderedAbbreviations)}";
+ return $@"(?.*?)\s?(?{abbreviationsPiped})";
+ }
+
+ ///
+ /// Parse a string given a particular regular expression.
+ ///
+ /// Error parsing string.
+ private TQuantity ParseWithRegex(string valueString,
+ string unitString,
+ QuantityFromDelegate fromDelegate,
+ IFormatProvider? formatProvider)
+ where TQuantity : IQuantity
+ where TUnitType : Enum
+ {
+ var value = double.Parse(valueString, ParseNumberStyles, formatProvider);
+ var parsedUnit = _unitParser.Parse(unitString, formatProvider);
+ return fromDelegate(value, parsedUnit);
+ }
+
+ ///
+ /// Parse a string given a particular regular expression.
+ ///
+ /// Error parsing string.
+ private bool TryParseWithRegex(string? valueString,
+ string? unitString,
+ QuantityFromDelegate fromDelegate,
+ IFormatProvider? formatProvider,
+ out TQuantity result)
+ where TQuantity : struct, IQuantity
+ where TUnitType : struct, Enum
+ {
+ result = default;
+
+ if (!double.TryParse(valueString, ParseNumberStyles, formatProvider, out var value))
+ return false;
+
+ if (!_unitParser.TryParse(unitString, formatProvider, out var parsedUnit))
+ return false;
+
+ result = fromDelegate(value, parsedUnit);
+ return true;
+ }
+
+ ///
+ /// Parse a string given a particular regular expression.
+ ///
+ /// Error parsing string.
+ private bool TryParseWithRegex(string? valueString,
+ string? unitString,
+ Type unitType,
+ QuantityFactory quantityFactory,
+ IFormatProvider? formatProvider,
+ out IQuantity? result)
+ {
+ result = default;
+
+ if (!double.TryParse(valueString, ParseNumberStyles, formatProvider, out var value)) return false;
+ if (!_unitParser.TryParse(unitString, unitType, formatProvider, out var parsedUnit)) return false;
+
+ result = quantityFactory(value, parsedUnit);
+ return true;
+ }
+
+ private static bool TryExtractValueAndUnit(Regex regex, string str, [NotNullWhen(true)] out string? valueString, [NotNullWhen(true)] out string? unitString)
+ {
+ var match = regex.Match(str);
+
+ // the regex coming in contains all allowed units as strings.
+ // That means if the unit in str is not formatted right
+ // the regex.Match will ether put str or string.empty into Groups[0] and Groups[1]
+ // Therefore a mismatch can be detected by comparing the values of this two groups.
+ if (match.Groups[0].Value == match.Groups[1].Value)
+ {
+ str = UnitParser.NormalizeUnitString(str);
+ match = regex.Match(str);
+ }
+
+ var groups = match.Groups;
+
+ var valueGroup = groups["value"];
+ var unitGroup = groups["unit"];
+ if (!valueGroup.Success || !unitGroup.Success)
+ {
+ valueString = null;
+ unitString = null;
+ return false;
+ }
+
+ valueString = valueGroup.Value;
+ unitString = unitGroup.Value;
+ return true;
+ }
+
+ private string CreateRegexPatternForQuantity(IFormatProvider? formatProvider) where TUnitType : Enum
+ {
+ IReadOnlyList unitAbbreviations = _unitAbbreviationsCache.GetAllUnitAbbreviationsForQuantity(typeof(TUnitType), formatProvider);
+ return GetRegexPatternForUnitAbbreviations(unitAbbreviations, matchEntireString: true);
+ }
+
+ private Regex CreateRegexForQuantity(IFormatProvider? formatProvider) where TUnitType : Enum
+ {
+ var pattern = CreateRegexPatternForQuantity(formatProvider);
+ return new Regex(pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
+ }
+
+ private static Regex CreateRegexForUnitAbbreviations(IEnumerable unitAbbreviations)
+ {
+ var pattern = GetRegexPatternForUnitAbbreviations(unitAbbreviations);
+ return new Regex(pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
+ }
+ }
+}
diff --git a/UnitsNet.Core/CustomCode/UnitAbbreviationsCache.cs b/UnitsNet.Core/CustomCode/UnitAbbreviationsCache.cs
new file mode 100644
index 0000000000..925f364bae
--- /dev/null
+++ b/UnitsNet.Core/CustomCode/UnitAbbreviationsCache.cs
@@ -0,0 +1,310 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+
+using UnitTypeToLookup = System.Collections.Generic.Dictionary;
+
+// ReSharper disable once CheckNamespace
+namespace UnitsNet
+{
+ ///
+ /// Cache of the mapping between unit enum values and unit abbreviation strings for one or more cultures.
+ /// A static instance is used internally for ToString() and Parse() of quantities and units.
+ ///
+ public sealed class UnitAbbreviationsCache
+ {
+ private readonly Dictionary _lookupsForCulture;
+
+ ///
+ /// Fallback culture used by and
+ /// if no abbreviations are found with a given culture.
+ ///
+ ///
+ /// User wants to call or with Russian
+ /// culture, but no translation is defined, so we return the US English definition as a last resort. If it's not
+ /// defined there either, an exception is thrown.
+ ///
+ internal static readonly CultureInfo FallbackCulture = new("en-US");
+
+ ///
+ /// The static instance used internally for ToString() and Parse() of quantities and units.
+ ///
+ public static UnitAbbreviationsCache Default { get; }
+
+ ///
+ /// Create an instance of the cache and load all the abbreviations defined in the library.
+ ///
+ public UnitAbbreviationsCache()
+ {
+ _lookupsForCulture = new Dictionary();
+
+ LoadGeneratedAbbreviations();
+ }
+
+ static UnitAbbreviationsCache()
+ {
+ Default = new UnitAbbreviationsCache();
+ }
+
+ private void LoadGeneratedAbbreviations()
+ {
+ foreach (TypeInfo quantityType in Quantity.QuantityTypes)
+ {
+ // TODO Cache reflection.
+ var mapGeneratedLocalizationsMethod = quantityType.GetMethod("MapGeneratedLocalizations", BindingFlags.NonPublic | BindingFlags.Static);
+ mapGeneratedLocalizationsMethod?.Invoke(null, new object[]{this});
+ }
+ }
+
+ ///
+ /// Adds one or more unit abbreviation for the given unit enum value.
+ /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums
+ /// in order to or on them later.
+ ///
+ /// The unit enum value.
+ /// Unit abbreviations to add.
+ /// The type of unit enum.
+ public void MapUnitToAbbreviation(TUnitType unit, params string[] abbreviations) where TUnitType : Enum
+ {
+ PerformAbbreviationMapping(unit, CultureInfo.CurrentCulture, false, true, abbreviations);
+ }
+
+ ///
+ /// Adds a unit abbreviation for the given unit enum value and sets it as the default.
+ /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums
+ /// in order to or on them later.
+ ///
+ /// The unit enum value.
+ /// Unit abbreviations to add as default.
+ /// The type of unit enum.
+ public void MapUnitToDefaultAbbreviation(TUnitType unit, string abbreviation) where TUnitType : Enum
+ {
+ PerformAbbreviationMapping(unit, CultureInfo.CurrentCulture, true, true, abbreviation);
+ }
+
+ ///
+ /// Adds one or more unit abbreviation for the given unit enum value.
+ /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums
+ /// in order to or on them later.
+ ///
+ /// The unit enum value.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Unit abbreviations to add.
+ /// The type of unit enum.
+ public void MapUnitToAbbreviation(TUnitType unit, IFormatProvider? formatProvider, params string[] abbreviations) where TUnitType : Enum
+ {
+ PerformAbbreviationMapping(unit, formatProvider, false, true, abbreviations);
+ }
+
+ ///
+ /// Adds a unit abbreviation for the given unit enum value and sets it as the default.
+ /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums
+ /// in order to or on them later.
+ ///
+ /// The unit enum value.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Unit abbreviation to add as default.
+ /// The type of unit enum.
+ public void MapUnitToDefaultAbbreviation(TUnitType unit, IFormatProvider? formatProvider, string abbreviation) where TUnitType : Enum
+ {
+ PerformAbbreviationMapping(unit, formatProvider, true, true, abbreviation);
+ }
+
+ ///
+ /// Adds one or more unit abbreviation for the given unit enum value.
+ /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums
+ /// in order to or on them later.
+ ///
+ /// The unit enum type.
+ /// The unit enum value.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Unit abbreviations to add.
+ public void MapUnitToAbbreviation(Type unitType, int unitValue, IFormatProvider? formatProvider, params string[] abbreviations)
+ {
+ var enumValue = (Enum)Enum.ToObject(unitType, unitValue);
+ PerformAbbreviationMapping(enumValue, formatProvider, false, true, abbreviations);
+ }
+
+ ///
+ /// Adds a unit abbreviation for the given unit enum value and sets it as the default.
+ /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums
+ /// in order to or on them later.
+ ///
+ /// The unit enum type.
+ /// The unit enum value.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Unit abbreviation to add as default.
+ public void MapUnitToDefaultAbbreviation(Type unitType, int unitValue, IFormatProvider? formatProvider, string abbreviation)
+ {
+ var enumValue = (Enum)Enum.ToObject(unitType, unitValue);
+ PerformAbbreviationMapping(enumValue, formatProvider, true, true, abbreviation);
+ }
+
+ internal void PerformAbbreviationMapping(Enum unitValue, IFormatProvider? formatProvider, bool setAsDefault, bool allowAbbreviationLookup, params string[] abbreviations)
+ {
+ if (abbreviations == null)
+ throw new ArgumentNullException(nameof(abbreviations));
+
+ formatProvider ??= CultureInfo.CurrentCulture;
+
+ if (!_lookupsForCulture.TryGetValue(formatProvider, out var quantitiesForProvider))
+ quantitiesForProvider = _lookupsForCulture[formatProvider] = new UnitTypeToLookup();
+
+ var unitType = unitValue.GetType();
+ if (!quantitiesForProvider.TryGetValue(unitType, out var unitToAbbreviations))
+ unitToAbbreviations = quantitiesForProvider[unitType] = new UnitValueAbbreviationLookup();
+
+ var unitValueAsInt = Convert.ToInt32(unitValue);
+ foreach (var abbr in abbreviations)
+ {
+ unitToAbbreviations.Add(unitValueAsInt, abbr, setAsDefault, allowAbbreviationLookup);
+ }
+ }
+
+ ///
+ /// Gets the default abbreviation for a given unit. If a unit has more than one abbreviation defined, then it returns the first one.
+ /// Example: GetDefaultAbbreviation<LengthUnit>(LengthUnit.Kilometer) => "km"
+ ///
+ /// The unit enum value.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// The type of unit enum.
+ /// The default unit abbreviation string.
+ public string GetDefaultAbbreviation(TUnitType unit, IFormatProvider? formatProvider = null) where TUnitType : Enum
+ {
+ var unitType = typeof(TUnitType);
+
+ if (!TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var lookup))
+ {
+ return !Equals(formatProvider, FallbackCulture)
+ ? GetDefaultAbbreviation(unit, FallbackCulture)
+ : throw new NotImplementedException($"No abbreviation is specified for {unitType.Name}.{unit}");
+ }
+
+ var abbreviations = lookup!.GetAbbreviationsForUnit(unit);
+ if (abbreviations.Count == 0)
+ {
+ return !Equals(formatProvider, FallbackCulture)
+ ? GetDefaultAbbreviation(unit, FallbackCulture)
+ : throw new NotImplementedException($"No abbreviation is specified for {unitType.Name}.{unit}");
+ }
+
+ return abbreviations.First();
+ }
+
+ ///
+ /// Gets the default abbreviation for a given unit type and its numeric enum value.
+ /// If a unit has more than one abbreviation defined, then it returns the first one.
+ /// Example: GetDefaultAbbreviation<LengthUnit>(typeof(LengthUnit), 1) => "cm"
+ ///
+ /// The unit enum type.
+ /// The unit enum value.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// The default unit abbreviation string.
+ public string GetDefaultAbbreviation(Type unitType, int unitValue, IFormatProvider? formatProvider = null)
+ {
+ if (!TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var lookup))
+ {
+ return !Equals(formatProvider, FallbackCulture)
+ ? GetDefaultAbbreviation(unitType, unitValue, FallbackCulture)
+ : throw new NotImplementedException($"No abbreviation is specified for {unitType.Name} with numeric value {unitValue}.");
+ }
+
+ var abbreviations = lookup!.GetAbbreviationsForUnit(unitValue);
+ if (abbreviations.Count == 0)
+ {
+ return !Equals(formatProvider, FallbackCulture)
+ ? GetDefaultAbbreviation(unitType, unitValue, FallbackCulture)
+ : throw new NotImplementedException($"No abbreviation is specified for {unitType.Name} with numeric value {unitValue}.");
+ }
+
+ return abbreviations.First();
+ }
+
+ ///
+ /// Get all abbreviations for unit.
+ ///
+ /// Enum type for units.
+ /// Enum value for unit.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Unit abbreviations associated with unit.
+ public string[] GetUnitAbbreviations(TUnitType unit, IFormatProvider? formatProvider = null) where TUnitType : Enum
+ {
+ return GetUnitAbbreviations(typeof(TUnitType), Convert.ToInt32(unit), formatProvider);
+ }
+
+ ///
+ /// Get all abbreviations for unit.
+ ///
+ /// Enum type for unit.
+ /// Enum value for unit.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Unit abbreviations associated with unit.
+ public string[] GetUnitAbbreviations(Type unitType, int unitValue, IFormatProvider? formatProvider = null)
+ {
+ formatProvider ??= CultureInfo.CurrentCulture;
+
+ if (!TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var lookup))
+ {
+ return !Equals(formatProvider, FallbackCulture)
+ ? GetUnitAbbreviations(unitType, unitValue, FallbackCulture)
+ : new string[] { };
+ }
+
+ var abbreviations = lookup!.GetAbbreviationsForUnit(unitValue);
+ if (abbreviations.Count == 0)
+ {
+ return !Equals(formatProvider, FallbackCulture)
+ ? GetUnitAbbreviations(unitType, unitValue, FallbackCulture)
+ : new string[] { };
+ }
+
+ return abbreviations.ToArray();
+ }
+
+ ///
+ /// Get all abbreviations for all units of a quantity.
+ ///
+ /// Enum type for unit.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Unit abbreviations associated with unit.
+ public IReadOnlyList GetAllUnitAbbreviationsForQuantity(Type unitEnumType, IFormatProvider? formatProvider = null)
+ {
+ formatProvider ??= CultureInfo.CurrentCulture;
+
+ if (!TryGetUnitValueAbbreviationLookup(unitEnumType, formatProvider, out var lookup))
+ {
+ return !Equals(formatProvider, FallbackCulture)
+ ? GetAllUnitAbbreviationsForQuantity(unitEnumType, FallbackCulture)
+ : new string[] { };
+ }
+
+ return lookup!.GetAllUnitAbbreviationsForQuantity();
+ }
+
+ internal bool TryGetUnitValueAbbreviationLookup(Type unitType, IFormatProvider? formatProvider, out UnitValueAbbreviationLookup? unitToAbbreviations)
+ {
+ unitToAbbreviations = null;
+
+ formatProvider ??= CultureInfo.CurrentCulture;
+
+ if (!_lookupsForCulture.TryGetValue(formatProvider, out UnitTypeToLookup? quantitiesForProvider))
+ {
+ return !Equals(formatProvider, FallbackCulture) &&
+ TryGetUnitValueAbbreviationLookup(unitType, FallbackCulture, out unitToAbbreviations);
+ }
+
+ if (!quantitiesForProvider.TryGetValue(unitType, out unitToAbbreviations))
+ {
+ return !Equals(formatProvider, FallbackCulture) &&
+ TryGetUnitValueAbbreviationLookup(unitType, FallbackCulture, out unitToAbbreviations);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/UnitsNet.Core/CustomCode/UnitParser.cs b/UnitsNet.Core/CustomCode/UnitParser.cs
new file mode 100644
index 0000000000..4762800779
--- /dev/null
+++ b/UnitsNet.Core/CustomCode/UnitParser.cs
@@ -0,0 +1,221 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+
+// ReSharper disable once CheckNamespace
+namespace UnitsNet
+{
+ ///
+ /// Parses units given a unit abbreviations cache.
+ /// The static instance is used internally to parse quantities and units using the
+ /// default abbreviations cache for all units and abbreviations defined in the library.
+ ///
+ public sealed class UnitParser
+ {
+ private readonly UnitAbbreviationsCache _unitAbbreviationsCache;
+
+ ///
+ /// The default static instance used internally to parse quantities and units using the
+ /// default abbreviations cache for all units and abbreviations defined in the library.
+ ///
+ public static UnitParser Default { get; }
+
+ ///
+ /// Create a parser using the given unit abbreviations cache.
+ ///
+ ///
+ public UnitParser(UnitAbbreviationsCache? unitAbbreviationsCache)
+ {
+ _unitAbbreviationsCache = unitAbbreviationsCache ?? UnitAbbreviationsCache.Default;
+ }
+
+ static UnitParser()
+ {
+ Default = new UnitParser(UnitAbbreviationsCache.Default);
+ }
+
+ ///
+ /// Parses a unit abbreviation for a given unit enumeration type.
+ /// Example: Parse<LengthUnit>("km") => LengthUnit.Kilometer
+ ///
+ ///
+ /// The format provider to use for lookup. Defaults to if null.
+ ///
+ ///
+ public TUnitType Parse(string unitAbbreviation, IFormatProvider? formatProvider = null) where TUnitType : Enum
+ {
+ return (TUnitType)Parse(unitAbbreviation, typeof(TUnitType), formatProvider);
+ }
+
+ ///
+ /// Parse a unit abbreviation, such as "kg" or "m", to the unit enum value of the enum type
+ /// .
+ ///
+ ///
+ /// Unit abbreviation, such as "kg" or "m" for and
+ /// respectively.
+ ///
+ /// Unit enum type, such as and .
+ /// The format provider to use for lookup. Defaults to if null.
+ /// Unit enum value, such as .
+ /// No units match the abbreviation.
+ /// More than one unit matches the abbreviation.
+ public Enum Parse(string? unitAbbreviation, Type unitType, IFormatProvider? formatProvider = null)
+ {
+ if (unitAbbreviation == null) throw new ArgumentNullException(nameof(unitAbbreviation));
+ unitAbbreviation = unitAbbreviation.Trim();
+
+ if (!_unitAbbreviationsCache.TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var abbreviations))
+ throw new UnitNotFoundException($"No abbreviations defined for unit type [{unitType}] for culture [{formatProvider}].");
+
+ var unitIntValues = abbreviations!.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: true);
+
+ if (unitIntValues.Count == 0)
+ {
+ unitAbbreviation = NormalizeUnitString(unitAbbreviation);
+ unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: true);
+ }
+
+ // Narrow the search if too many hits, for example Megabar "Mbar" and Millibar "mbar" need to be distinguished
+ if (unitIntValues.Count > 1)
+ unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: false);
+
+ switch (unitIntValues.Count)
+ {
+ case 1:
+ return (Enum) Enum.ToObject(unitType, unitIntValues[0]);
+ case 0:
+ // Retry with fallback culture, if different.
+ if (!Equals(formatProvider, UnitAbbreviationsCache.FallbackCulture))
+ {
+ return Parse(unitAbbreviation, unitType, UnitAbbreviationsCache.FallbackCulture);
+ }
+
+ throw new UnitNotFoundException($"Unit not found with abbreviation [{unitAbbreviation}] for unit type [{unitType}].");
+ default:
+ string unitsCsv = string.Join(", ", unitIntValues.Select(x => Enum.GetName(unitType, x)).ToArray());
+ throw new AmbiguousUnitParseException(
+ $"Cannot parse \"{unitAbbreviation}\" since it could be either of these: {unitsCsv}");
+ }
+ }
+
+ internal static string NormalizeUnitString(string unitAbbreviation)
+ {
+ // remove all whitespace in the string
+ unitAbbreviation = new string(unitAbbreviation.Where(c => !char.IsWhiteSpace(c)).ToArray());
+
+ unitAbbreviation = unitAbbreviation.Replace("^-9", "⁻⁹");
+ unitAbbreviation = unitAbbreviation.Replace("^-8", "⁻⁸");
+ unitAbbreviation = unitAbbreviation.Replace("^-7", "⁻⁷");
+ unitAbbreviation = unitAbbreviation.Replace("^-6", "⁻⁶");
+ unitAbbreviation = unitAbbreviation.Replace("^-5", "⁻⁵");
+ unitAbbreviation = unitAbbreviation.Replace("^-4", "⁻⁴");
+ unitAbbreviation = unitAbbreviation.Replace("^-3", "⁻³");
+ unitAbbreviation = unitAbbreviation.Replace("^-2", "⁻²");
+ unitAbbreviation = unitAbbreviation.Replace("^-1", "⁻¹");
+ unitAbbreviation = unitAbbreviation.Replace("^1", "");
+ unitAbbreviation = unitAbbreviation.Replace("^2", "²");
+ unitAbbreviation = unitAbbreviation.Replace("^3", "³");
+ unitAbbreviation = unitAbbreviation.Replace("^4", "⁴");
+ unitAbbreviation = unitAbbreviation.Replace("^5", "⁵");
+ unitAbbreviation = unitAbbreviation.Replace("^6", "⁶");
+ unitAbbreviation = unitAbbreviation.Replace("^7", "⁷");
+ unitAbbreviation = unitAbbreviation.Replace("^8", "⁸");
+ unitAbbreviation = unitAbbreviation.Replace("^9", "⁹");
+ unitAbbreviation = unitAbbreviation.Replace("*", "·");
+ // "\u03bc" = Lower case Greek letter 'Mu'
+ // "\u00b5" = Micro sign
+ unitAbbreviation = unitAbbreviation.Replace("\u03bc", "\u00b5");
+
+ return unitAbbreviation;
+ }
+
+ ///
+ /// Try to parse a unit abbreviation.
+ ///
+ /// The string value.
+ /// The unit enum value as out result.
+ /// Type of unit enum.
+ /// True if successful.
+ public bool TryParse(string unitAbbreviation, out TUnitType unit) where TUnitType : struct, Enum
+ {
+ return TryParse(unitAbbreviation, null, out unit);
+ }
+
+ ///
+ /// Try to parse a unit abbreviation.
+ ///
+ /// The string value.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// The unit enum value as out result.
+ /// Type of unit enum.
+ /// True if successful.
+ public bool TryParse(string? unitAbbreviation, IFormatProvider? formatProvider, out TUnitType unit) where TUnitType : struct, Enum
+ {
+ unit = default;
+
+ if (!TryParse(unitAbbreviation, typeof(TUnitType), formatProvider, out var unitObj))
+ return false;
+
+ unit = (TUnitType)unitObj;
+ return true;
+ }
+
+ ///
+ /// Try to parse a unit abbreviation.
+ ///
+ /// The string value.
+ /// Type of unit enum.
+ /// The unit enum value as out result.
+ /// True if successful.
+ public bool TryParse(string unitAbbreviation, Type unitType, [NotNullWhen(true)] out Enum? unit)
+ {
+ return TryParse(unitAbbreviation, unitType, null, out unit);
+ }
+
+ ///
+ /// Try to parse a unit abbreviation.
+ ///
+ /// The string value.
+ /// Type of unit enum.
+ /// The format provider to use for lookup. Defaults to if null.
+ /// The unit enum value as out result.
+ /// True if successful.
+ public bool TryParse(string? unitAbbreviation, Type unitType, IFormatProvider? formatProvider, [NotNullWhen(true)] out Enum? unit)
+ {
+ if (unitAbbreviation == null)
+ {
+ unit = default;
+ return false;
+ }
+
+ unitAbbreviation = unitAbbreviation.Trim();
+ unit = default;
+
+ if (!_unitAbbreviationsCache.TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var abbreviations))
+ return false;
+
+ var unitIntValues = abbreviations!.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: true);
+
+ if (unitIntValues.Count == 0)
+ {
+ unitAbbreviation = NormalizeUnitString(unitAbbreviation);
+ unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: true);
+ }
+
+ // Narrow the search if too many hits, for example Megabar "Mbar" and Millibar "mbar" need to be distinguished
+ if (unitIntValues.Count > 1)
+ unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: false);
+
+ if (unitIntValues.Count != 1)
+ return false;
+
+ unit = (Enum)Enum.ToObject(unitType, unitIntValues[0]);
+ return true;
+ }
+ }
+}
diff --git a/UnitsNet.Core/CustomCode/UnitValueAbbreviationLookup.cs b/UnitsNet.Core/CustomCode/UnitValueAbbreviationLookup.cs
new file mode 100644
index 0000000000..d303658682
--- /dev/null
+++ b/UnitsNet.Core/CustomCode/UnitValueAbbreviationLookup.cs
@@ -0,0 +1,94 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace UnitsNet
+{
+ internal class UnitToAbbreviationMap : ConcurrentDictionary> { }
+ internal class AbbreviationToUnitMap : ConcurrentDictionary> { }
+
+ internal class UnitValueAbbreviationLookup
+ {
+ private readonly UnitToAbbreviationMap _unitToAbbreviationMap = new();
+ private readonly AbbreviationToUnitMap _abbreviationToUnitMap = new();
+ private readonly AbbreviationToUnitMap _lowerCaseAbbreviationToUnitMap = new();
+ private Lazy _allUnitAbbreviationsLazy;
+ private readonly object _syncRoot = new();
+
+ public UnitValueAbbreviationLookup()
+ {
+ _allUnitAbbreviationsLazy = new Lazy(ComputeAllUnitAbbreviationsValue);
+ }
+
+ internal IReadOnlyList GetAllUnitAbbreviationsForQuantity()
+ {
+ return _allUnitAbbreviationsLazy.Value;
+ }
+
+ internal IReadOnlyList GetAbbreviationsForUnit(TUnitType unit) where TUnitType : Enum
+ {
+ return GetAbbreviationsForUnit(Convert.ToInt32(unit));
+ }
+
+ internal IReadOnlyList GetAbbreviationsForUnit(int unit)
+ {
+ if (!_unitToAbbreviationMap.TryGetValue(unit, out var abbreviations))
+ return new List(0);
+
+ return abbreviations.ToList();
+ }
+
+ internal IReadOnlyList GetUnitsForAbbreviation(string abbreviation, bool ignoreCase)
+ {
+ var lowerCaseAbbreviation = abbreviation.ToLower();
+ var key = ignoreCase ? lowerCaseAbbreviation : abbreviation;
+ var map = ignoreCase ? _lowerCaseAbbreviationToUnitMap : _abbreviationToUnitMap;
+
+ if (!map.TryGetValue(key, out IReadOnlyList? units))
+ return new List(0);
+
+ return units.ToList();
+ }
+
+ internal void Add(int unit, string abbreviation, bool setAsDefault = false, bool allowAbbreviationLookup = true)
+ {
+ // Restrict concurrency on writes.
+ // By using ConcurrencyDictionary and immutable IReadOnlyList instances, we don't need to lock on reads.
+ lock (_syncRoot)
+ {
+ var lowerCaseAbbreviation = abbreviation.ToLower();
+
+ if (allowAbbreviationLookup)
+ {
+ _abbreviationToUnitMap.AddOrUpdate(abbreviation,
+ addValueFactory: _ => new List { unit },
+ updateValueFactory: (_, existing) => existing.Append(unit).Distinct().ToList());
+
+ _lowerCaseAbbreviationToUnitMap.AddOrUpdate(lowerCaseAbbreviation,
+ addValueFactory: _ => new List { unit },
+ updateValueFactory: (_, existing) => existing.Append(unit).Distinct().ToList());
+ }
+
+ _unitToAbbreviationMap.AddOrUpdate(unit,
+ addValueFactory: _ => new List { abbreviation },
+ updateValueFactory: (_, existing) =>
+ {
+ return setAsDefault
+ ? existing.Where(x => x != abbreviation).Prepend(abbreviation).ToList()
+ : existing.Where(x => x != abbreviation).Append(abbreviation).ToList();
+ });
+
+ _allUnitAbbreviationsLazy = new Lazy(ComputeAllUnitAbbreviationsValue);
+ }
+ }
+
+ private string[] ComputeAllUnitAbbreviationsValue()
+ {
+ return _unitToAbbreviationMap.Values.SelectMany(abbreviations => abbreviations).Distinct().ToArray();
+ }
+ }
+}
diff --git a/UnitsNet.Core/GeneratedCode/Quantity.g.cs b/UnitsNet.Core/GeneratedCode/Quantity.g.cs
new file mode 100644
index 0000000000..627011fdea
--- /dev/null
+++ b/UnitsNet.Core/GeneratedCode/Quantity.g.cs
@@ -0,0 +1,913 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by \generate-code.bat.
+//
+// Changes to this file will be lost when the code is regenerated.
+// The build server regenerates the code before each build and a pre-build
+// step will regenerate the code on each local build.
+//
+// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units.
+//
+// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities.
+// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities.
+//
+//
+//------------------------------------------------------------------------------
+
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using System;
+using System.Globalization;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+#nullable enable
+
+namespace UnitsNet
+{
+ ///
+ /// Dynamically parse or construct quantities when types are only known at runtime.
+ ///
+ public static partial class Quantity
+ {
+ // ///
+ // /// All QuantityInfo instances mapped by quantity name that are present in UnitsNet by default.
+ // ///
+ // public static readonly IDictionary ByName = new Dictionary
+ // {
+ // { "Acceleration", Acceleration.Info },
+ // { "AmountOfSubstance", AmountOfSubstance.Info },
+ // { "AmplitudeRatio", AmplitudeRatio.Info },
+ // { "Angle", Angle.Info },
+ // { "ApparentEnergy", ApparentEnergy.Info },
+ // { "ApparentPower", ApparentPower.Info },
+ // { "Area", Area.Info },
+ // { "AreaDensity", AreaDensity.Info },
+ // { "AreaMomentOfInertia", AreaMomentOfInertia.Info },
+ // { "BitRate", BitRate.Info },
+ // { "BrakeSpecificFuelConsumption", BrakeSpecificFuelConsumption.Info },
+ // { "Capacitance", Capacitance.Info },
+ // { "CoefficientOfThermalExpansion", CoefficientOfThermalExpansion.Info },
+ // { "Compressibility", Compressibility.Info },
+ // { "Density", Density.Info },
+ // { "Duration", Duration.Info },
+ // { "DynamicViscosity", DynamicViscosity.Info },
+ // { "ElectricAdmittance", ElectricAdmittance.Info },
+ // { "ElectricCharge", ElectricCharge.Info },
+ // { "ElectricChargeDensity", ElectricChargeDensity.Info },
+ // { "ElectricConductance", ElectricConductance.Info },
+ // { "ElectricConductivity", ElectricConductivity.Info },
+ // { "ElectricCurrent", ElectricCurrent.Info },
+ // { "ElectricCurrentDensity", ElectricCurrentDensity.Info },
+ // { "ElectricCurrentGradient", ElectricCurrentGradient.Info },
+ // { "ElectricField", ElectricField.Info },
+ // { "ElectricInductance", ElectricInductance.Info },
+ // { "ElectricPotential", ElectricPotential.Info },
+ // { "ElectricPotentialAc", ElectricPotentialAc.Info },
+ // { "ElectricPotentialChangeRate", ElectricPotentialChangeRate.Info },
+ // { "ElectricPotentialDc", ElectricPotentialDc.Info },
+ // { "ElectricResistance", ElectricResistance.Info },
+ // { "ElectricResistivity", ElectricResistivity.Info },
+ // { "ElectricSurfaceChargeDensity", ElectricSurfaceChargeDensity.Info },
+ // { "Energy", Energy.Info },
+ // { "EnergyDensity", EnergyDensity.Info },
+ // { "Entropy", Entropy.Info },
+ // { "Force", Force.Info },
+ // { "ForceChangeRate", ForceChangeRate.Info },
+ // { "ForcePerLength", ForcePerLength.Info },
+ // { "Frequency", Frequency.Info },
+ // { "FuelEfficiency", FuelEfficiency.Info },
+ // { "HeatFlux", HeatFlux.Info },
+ // { "HeatTransferCoefficient", HeatTransferCoefficient.Info },
+ // { "Illuminance", Illuminance.Info },
+ // { "Information", Information.Info },
+ // { "Irradiance", Irradiance.Info },
+ // { "Irradiation", Irradiation.Info },
+ // { "Jerk", Jerk.Info },
+ // { "KinematicViscosity", KinematicViscosity.Info },
+ // { "Length", Length.Info },
+ // { "Level", Level.Info },
+ // { "LinearDensity", LinearDensity.Info },
+ // { "LinearPowerDensity", LinearPowerDensity.Info },
+ // { "Luminance", Luminance.Info },
+ // { "Luminosity", Luminosity.Info },
+ // { "LuminousFlux", LuminousFlux.Info },
+ // { "LuminousIntensity", LuminousIntensity.Info },
+ // { "MagneticField", MagneticField.Info },
+ // { "MagneticFlux", MagneticFlux.Info },
+ // { "Magnetization", Magnetization.Info },
+ // { "Mass", Mass.Info },
+ // { "MassConcentration", MassConcentration.Info },
+ // { "MassFlow", MassFlow.Info },
+ // { "MassFlux", MassFlux.Info },
+ // { "MassFraction", MassFraction.Info },
+ // { "MassMomentOfInertia", MassMomentOfInertia.Info },
+ // { "MolarEnergy", MolarEnergy.Info },
+ // { "MolarEntropy", MolarEntropy.Info },
+ // { "Molarity", Molarity.Info },
+ // { "MolarMass", MolarMass.Info },
+ // { "Permeability", Permeability.Info },
+ // { "Permittivity", Permittivity.Info },
+ // { "PorousMediumPermeability", PorousMediumPermeability.Info },
+ // { "Power", Power.Info },
+ // { "PowerDensity", PowerDensity.Info },
+ // { "PowerRatio", PowerRatio.Info },
+ // { "Pressure", Pressure.Info },
+ // { "PressureChangeRate", PressureChangeRate.Info },
+ // { "Ratio", Ratio.Info },
+ // { "RatioChangeRate", RatioChangeRate.Info },
+ // { "ReactiveEnergy", ReactiveEnergy.Info },
+ // { "ReactivePower", ReactivePower.Info },
+ // { "ReciprocalArea", ReciprocalArea.Info },
+ // { "ReciprocalLength", ReciprocalLength.Info },
+ // { "RelativeHumidity", RelativeHumidity.Info },
+ // { "RotationalAcceleration", RotationalAcceleration.Info },
+ // { "RotationalSpeed", RotationalSpeed.Info },
+ // { "RotationalStiffness", RotationalStiffness.Info },
+ // { "RotationalStiffnessPerLength", RotationalStiffnessPerLength.Info },
+ // { "Scalar", Scalar.Info },
+ // { "SolidAngle", SolidAngle.Info },
+ // { "SpecificEnergy", SpecificEnergy.Info },
+ // { "SpecificEntropy", SpecificEntropy.Info },
+ // { "SpecificFuelConsumption", SpecificFuelConsumption.Info },
+ // { "SpecificVolume", SpecificVolume.Info },
+ // { "SpecificWeight", SpecificWeight.Info },
+ // { "Speed", Speed.Info },
+ // { "StandardVolumeFlow", StandardVolumeFlow.Info },
+ // { "Temperature", Temperature.Info },
+ // { "TemperatureChangeRate", TemperatureChangeRate.Info },
+ // { "TemperatureDelta", TemperatureDelta.Info },
+ // { "TemperatureGradient", TemperatureGradient.Info },
+ // { "ThermalConductivity", ThermalConductivity.Info },
+ // { "ThermalResistance", ThermalResistance.Info },
+ // { "Torque", Torque.Info },
+ // { "TorquePerLength", TorquePerLength.Info },
+ // { "Turbidity", Turbidity.Info },
+ // { "VitaminA", VitaminA.Info },
+ // { "Volume", Volume.Info },
+ // { "VolumeConcentration", VolumeConcentration.Info },
+ // { "VolumeFlow", VolumeFlow.Info },
+ // { "VolumeFlowPerArea", VolumeFlowPerArea.Info },
+ // { "VolumePerLength", VolumePerLength.Info },
+ // { "VolumetricHeatCapacity", VolumetricHeatCapacity.Info },
+ // { "WarpingMomentOfInertia", WarpingMomentOfInertia.Info },
+ // };
+ //
+ // ///
+ // /// Dynamically constructs a quantity of the given with the value in the quantity's base units.
+ // ///
+ // /// The of the quantity to create.
+ // /// The value to construct the quantity with.
+ // /// The created quantity.
+ // public static IQuantity FromQuantityInfo(QuantityInfo quantityInfo, QuantityValue value)
+ // {
+ // return quantityInfo.Name switch
+ // {
+ // "Acceleration" => Acceleration.From(value, Acceleration.BaseUnit),
+ // "AmountOfSubstance" => AmountOfSubstance.From(value, AmountOfSubstance.BaseUnit),
+ // "AmplitudeRatio" => AmplitudeRatio.From(value, AmplitudeRatio.BaseUnit),
+ // "Angle" => Angle.From(value, Angle.BaseUnit),
+ // "ApparentEnergy" => ApparentEnergy.From(value, ApparentEnergy.BaseUnit),
+ // "ApparentPower" => ApparentPower.From(value, ApparentPower.BaseUnit),
+ // "Area" => Area.From(value, Area.BaseUnit),
+ // "AreaDensity" => AreaDensity.From(value, AreaDensity.BaseUnit),
+ // "AreaMomentOfInertia" => AreaMomentOfInertia.From(value, AreaMomentOfInertia.BaseUnit),
+ // "BitRate" => BitRate.From(value, BitRate.BaseUnit),
+ // "BrakeSpecificFuelConsumption" => BrakeSpecificFuelConsumption.From(value, BrakeSpecificFuelConsumption.BaseUnit),
+ // "Capacitance" => Capacitance.From(value, Capacitance.BaseUnit),
+ // "CoefficientOfThermalExpansion" => CoefficientOfThermalExpansion.From(value, CoefficientOfThermalExpansion.BaseUnit),
+ // "Compressibility" => Compressibility.From(value, Compressibility.BaseUnit),
+ // "Density" => Density.From(value, Density.BaseUnit),
+ // "Duration" => Duration.From(value, Duration.BaseUnit),
+ // "DynamicViscosity" => DynamicViscosity.From(value, DynamicViscosity.BaseUnit),
+ // "ElectricAdmittance" => ElectricAdmittance.From(value, ElectricAdmittance.BaseUnit),
+ // "ElectricCharge" => ElectricCharge.From(value, ElectricCharge.BaseUnit),
+ // "ElectricChargeDensity" => ElectricChargeDensity.From(value, ElectricChargeDensity.BaseUnit),
+ // "ElectricConductance" => ElectricConductance.From(value, ElectricConductance.BaseUnit),
+ // "ElectricConductivity" => ElectricConductivity.From(value, ElectricConductivity.BaseUnit),
+ // "ElectricCurrent" => ElectricCurrent.From(value, ElectricCurrent.BaseUnit),
+ // "ElectricCurrentDensity" => ElectricCurrentDensity.From(value, ElectricCurrentDensity.BaseUnit),
+ // "ElectricCurrentGradient" => ElectricCurrentGradient.From(value, ElectricCurrentGradient.BaseUnit),
+ // "ElectricField" => ElectricField.From(value, ElectricField.BaseUnit),
+ // "ElectricInductance" => ElectricInductance.From(value, ElectricInductance.BaseUnit),
+ // "ElectricPotential" => ElectricPotential.From(value, ElectricPotential.BaseUnit),
+ // "ElectricPotentialAc" => ElectricPotentialAc.From(value, ElectricPotentialAc.BaseUnit),
+ // "ElectricPotentialChangeRate" => ElectricPotentialChangeRate.From(value, ElectricPotentialChangeRate.BaseUnit),
+ // "ElectricPotentialDc" => ElectricPotentialDc.From(value, ElectricPotentialDc.BaseUnit),
+ // "ElectricResistance" => ElectricResistance.From(value, ElectricResistance.BaseUnit),
+ // "ElectricResistivity" => ElectricResistivity.From(value, ElectricResistivity.BaseUnit),
+ // "ElectricSurfaceChargeDensity" => ElectricSurfaceChargeDensity.From(value, ElectricSurfaceChargeDensity.BaseUnit),
+ // "Energy" => Energy.From(value, Energy.BaseUnit),
+ // "EnergyDensity" => EnergyDensity.From(value, EnergyDensity.BaseUnit),
+ // "Entropy" => Entropy.From(value, Entropy.BaseUnit),
+ // "Force" => Force.From(value, Force.BaseUnit),
+ // "ForceChangeRate" => ForceChangeRate.From(value, ForceChangeRate.BaseUnit),
+ // "ForcePerLength" => ForcePerLength.From(value, ForcePerLength.BaseUnit),
+ // "Frequency" => Frequency.From(value, Frequency.BaseUnit),
+ // "FuelEfficiency" => FuelEfficiency.From(value, FuelEfficiency.BaseUnit),
+ // "HeatFlux" => HeatFlux.From(value, HeatFlux.BaseUnit),
+ // "HeatTransferCoefficient" => HeatTransferCoefficient.From(value, HeatTransferCoefficient.BaseUnit),
+ // "Illuminance" => Illuminance.From(value, Illuminance.BaseUnit),
+ // "Information" => Information.From(value, Information.BaseUnit),
+ // "Irradiance" => Irradiance.From(value, Irradiance.BaseUnit),
+ // "Irradiation" => Irradiation.From(value, Irradiation.BaseUnit),
+ // "Jerk" => Jerk.From(value, Jerk.BaseUnit),
+ // "KinematicViscosity" => KinematicViscosity.From(value, KinematicViscosity.BaseUnit),
+ // "Length" => Length.From(value, Length.BaseUnit),
+ // "Level" => Level.From(value, Level.BaseUnit),
+ // "LinearDensity" => LinearDensity.From(value, LinearDensity.BaseUnit),
+ // "LinearPowerDensity" => LinearPowerDensity.From(value, LinearPowerDensity.BaseUnit),
+ // "Luminance" => Luminance.From(value, Luminance.BaseUnit),
+ // "Luminosity" => Luminosity.From(value, Luminosity.BaseUnit),
+ // "LuminousFlux" => LuminousFlux.From(value, LuminousFlux.BaseUnit),
+ // "LuminousIntensity" => LuminousIntensity.From(value, LuminousIntensity.BaseUnit),
+ // "MagneticField" => MagneticField.From(value, MagneticField.BaseUnit),
+ // "MagneticFlux" => MagneticFlux.From(value, MagneticFlux.BaseUnit),
+ // "Magnetization" => Magnetization.From(value, Magnetization.BaseUnit),
+ // "Mass" => Mass.From(value, Mass.BaseUnit),
+ // "MassConcentration" => MassConcentration.From(value, MassConcentration.BaseUnit),
+ // "MassFlow" => MassFlow.From(value, MassFlow.BaseUnit),
+ // "MassFlux" => MassFlux.From(value, MassFlux.BaseUnit),
+ // "MassFraction" => MassFraction.From(value, MassFraction.BaseUnit),
+ // "MassMomentOfInertia" => MassMomentOfInertia.From(value, MassMomentOfInertia.BaseUnit),
+ // "MolarEnergy" => MolarEnergy.From(value, MolarEnergy.BaseUnit),
+ // "MolarEntropy" => MolarEntropy.From(value, MolarEntropy.BaseUnit),
+ // "Molarity" => Molarity.From(value, Molarity.BaseUnit),
+ // "MolarMass" => MolarMass.From(value, MolarMass.BaseUnit),
+ // "Permeability" => Permeability.From(value, Permeability.BaseUnit),
+ // "Permittivity" => Permittivity.From(value, Permittivity.BaseUnit),
+ // "PorousMediumPermeability" => PorousMediumPermeability.From(value, PorousMediumPermeability.BaseUnit),
+ // "Power" => Power.From(value, Power.BaseUnit),
+ // "PowerDensity" => PowerDensity.From(value, PowerDensity.BaseUnit),
+ // "PowerRatio" => PowerRatio.From(value, PowerRatio.BaseUnit),
+ // "Pressure" => Pressure.From(value, Pressure.BaseUnit),
+ // "PressureChangeRate" => PressureChangeRate.From(value, PressureChangeRate.BaseUnit),
+ // "Ratio" => Ratio.From(value, Ratio.BaseUnit),
+ // "RatioChangeRate" => RatioChangeRate.From(value, RatioChangeRate.BaseUnit),
+ // "ReactiveEnergy" => ReactiveEnergy.From(value, ReactiveEnergy.BaseUnit),
+ // "ReactivePower" => ReactivePower.From(value, ReactivePower.BaseUnit),
+ // "ReciprocalArea" => ReciprocalArea.From(value, ReciprocalArea.BaseUnit),
+ // "ReciprocalLength" => ReciprocalLength.From(value, ReciprocalLength.BaseUnit),
+ // "RelativeHumidity" => RelativeHumidity.From(value, RelativeHumidity.BaseUnit),
+ // "RotationalAcceleration" => RotationalAcceleration.From(value, RotationalAcceleration.BaseUnit),
+ // "RotationalSpeed" => RotationalSpeed.From(value, RotationalSpeed.BaseUnit),
+ // "RotationalStiffness" => RotationalStiffness.From(value, RotationalStiffness.BaseUnit),
+ // "RotationalStiffnessPerLength" => RotationalStiffnessPerLength.From(value, RotationalStiffnessPerLength.BaseUnit),
+ // "Scalar" => Scalar.From(value, Scalar.BaseUnit),
+ // "SolidAngle" => SolidAngle.From(value, SolidAngle.BaseUnit),
+ // "SpecificEnergy" => SpecificEnergy.From(value, SpecificEnergy.BaseUnit),
+ // "SpecificEntropy" => SpecificEntropy.From(value, SpecificEntropy.BaseUnit),
+ // "SpecificFuelConsumption" => SpecificFuelConsumption.From(value, SpecificFuelConsumption.BaseUnit),
+ // "SpecificVolume" => SpecificVolume.From(value, SpecificVolume.BaseUnit),
+ // "SpecificWeight" => SpecificWeight.From(value, SpecificWeight.BaseUnit),
+ // "Speed" => Speed.From(value, Speed.BaseUnit),
+ // "StandardVolumeFlow" => StandardVolumeFlow.From(value, StandardVolumeFlow.BaseUnit),
+ // "Temperature" => Temperature.From(value, Temperature.BaseUnit),
+ // "TemperatureChangeRate" => TemperatureChangeRate.From(value, TemperatureChangeRate.BaseUnit),
+ // "TemperatureDelta" => TemperatureDelta.From(value, TemperatureDelta.BaseUnit),
+ // "TemperatureGradient" => TemperatureGradient.From(value, TemperatureGradient.BaseUnit),
+ // "ThermalConductivity" => ThermalConductivity.From(value, ThermalConductivity.BaseUnit),
+ // "ThermalResistance" => ThermalResistance.From(value, ThermalResistance.BaseUnit),
+ // "Torque" => Torque.From(value, Torque.BaseUnit),
+ // "TorquePerLength" => TorquePerLength.From(value, TorquePerLength.BaseUnit),
+ // "Turbidity" => Turbidity.From(value, Turbidity.BaseUnit),
+ // "VitaminA" => VitaminA.From(value, VitaminA.BaseUnit),
+ // "Volume" => Volume.From(value, Volume.BaseUnit),
+ // "VolumeConcentration" => VolumeConcentration.From(value, VolumeConcentration.BaseUnit),
+ // "VolumeFlow" => VolumeFlow.From(value, VolumeFlow.BaseUnit),
+ // "VolumeFlowPerArea" => VolumeFlowPerArea.From(value, VolumeFlowPerArea.BaseUnit),
+ // "VolumePerLength" => VolumePerLength.From(value, VolumePerLength.BaseUnit),
+ // "VolumetricHeatCapacity" => VolumetricHeatCapacity.From(value, VolumetricHeatCapacity.BaseUnit),
+ // "WarpingMomentOfInertia" => WarpingMomentOfInertia.From(value, WarpingMomentOfInertia.BaseUnit),
+ // _ => throw new ArgumentException($"{quantityInfo.Name} is not a supported quantity.")
+ // };
+ // }
+ //
+ // ///
+ // /// Try to dynamically construct a quantity.
+ // ///
+ // /// Numeric value.
+ // /// Unit enum value.
+ // /// The resulting quantity if successful, otherwise default.
+ // /// True if successful with assigned the value, otherwise false.
+ // public static bool TryFrom(QuantityValue value, Enum unit, [NotNullWhen(true)] out IQuantity? quantity)
+ // {
+ // switch (unit)
+ // {
+ // case AccelerationUnit accelerationUnit:
+ // quantity = Acceleration.From(value, accelerationUnit);
+ // return true;
+ // case AmountOfSubstanceUnit amountOfSubstanceUnit:
+ // quantity = AmountOfSubstance.From(value, amountOfSubstanceUnit);
+ // return true;
+ // case AmplitudeRatioUnit amplitudeRatioUnit:
+ // quantity = AmplitudeRatio.From(value, amplitudeRatioUnit);
+ // return true;
+ // case AngleUnit angleUnit:
+ // quantity = Angle.From(value, angleUnit);
+ // return true;
+ // case ApparentEnergyUnit apparentEnergyUnit:
+ // quantity = ApparentEnergy.From(value, apparentEnergyUnit);
+ // return true;
+ // case ApparentPowerUnit apparentPowerUnit:
+ // quantity = ApparentPower.From(value, apparentPowerUnit);
+ // return true;
+ // case AreaUnit areaUnit:
+ // quantity = Area.From(value, areaUnit);
+ // return true;
+ // case AreaDensityUnit areaDensityUnit:
+ // quantity = AreaDensity.From(value, areaDensityUnit);
+ // return true;
+ // case AreaMomentOfInertiaUnit areaMomentOfInertiaUnit:
+ // quantity = AreaMomentOfInertia.From(value, areaMomentOfInertiaUnit);
+ // return true;
+ // case BitRateUnit bitRateUnit:
+ // quantity = BitRate.From(value, bitRateUnit);
+ // return true;
+ // case BrakeSpecificFuelConsumptionUnit brakeSpecificFuelConsumptionUnit:
+ // quantity = BrakeSpecificFuelConsumption.From(value, brakeSpecificFuelConsumptionUnit);
+ // return true;
+ // case CapacitanceUnit capacitanceUnit:
+ // quantity = Capacitance.From(value, capacitanceUnit);
+ // return true;
+ // case CoefficientOfThermalExpansionUnit coefficientOfThermalExpansionUnit:
+ // quantity = CoefficientOfThermalExpansion.From(value, coefficientOfThermalExpansionUnit);
+ // return true;
+ // case CompressibilityUnit compressibilityUnit:
+ // quantity = Compressibility.From(value, compressibilityUnit);
+ // return true;
+ // case DensityUnit densityUnit:
+ // quantity = Density.From(value, densityUnit);
+ // return true;
+ // case DurationUnit durationUnit:
+ // quantity = Duration.From(value, durationUnit);
+ // return true;
+ // case DynamicViscosityUnit dynamicViscosityUnit:
+ // quantity = DynamicViscosity.From(value, dynamicViscosityUnit);
+ // return true;
+ // case ElectricAdmittanceUnit electricAdmittanceUnit:
+ // quantity = ElectricAdmittance.From(value, electricAdmittanceUnit);
+ // return true;
+ // case ElectricChargeUnit electricChargeUnit:
+ // quantity = ElectricCharge.From(value, electricChargeUnit);
+ // return true;
+ // case ElectricChargeDensityUnit electricChargeDensityUnit:
+ // quantity = ElectricChargeDensity.From(value, electricChargeDensityUnit);
+ // return true;
+ // case ElectricConductanceUnit electricConductanceUnit:
+ // quantity = ElectricConductance.From(value, electricConductanceUnit);
+ // return true;
+ // case ElectricConductivityUnit electricConductivityUnit:
+ // quantity = ElectricConductivity.From(value, electricConductivityUnit);
+ // return true;
+ // case ElectricCurrentUnit electricCurrentUnit:
+ // quantity = ElectricCurrent.From(value, electricCurrentUnit);
+ // return true;
+ // case ElectricCurrentDensityUnit electricCurrentDensityUnit:
+ // quantity = ElectricCurrentDensity.From(value, electricCurrentDensityUnit);
+ // return true;
+ // case ElectricCurrentGradientUnit electricCurrentGradientUnit:
+ // quantity = ElectricCurrentGradient.From(value, electricCurrentGradientUnit);
+ // return true;
+ // case ElectricFieldUnit electricFieldUnit:
+ // quantity = ElectricField.From(value, electricFieldUnit);
+ // return true;
+ // case ElectricInductanceUnit electricInductanceUnit:
+ // quantity = ElectricInductance.From(value, electricInductanceUnit);
+ // return true;
+ // case ElectricPotentialUnit electricPotentialUnit:
+ // quantity = ElectricPotential.From(value, electricPotentialUnit);
+ // return true;
+ // case ElectricPotentialAcUnit electricPotentialAcUnit:
+ // quantity = ElectricPotentialAc.From(value, electricPotentialAcUnit);
+ // return true;
+ // case ElectricPotentialChangeRateUnit electricPotentialChangeRateUnit:
+ // quantity = ElectricPotentialChangeRate.From(value, electricPotentialChangeRateUnit);
+ // return true;
+ // case ElectricPotentialDcUnit electricPotentialDcUnit:
+ // quantity = ElectricPotentialDc.From(value, electricPotentialDcUnit);
+ // return true;
+ // case ElectricResistanceUnit electricResistanceUnit:
+ // quantity = ElectricResistance.From(value, electricResistanceUnit);
+ // return true;
+ // case ElectricResistivityUnit electricResistivityUnit:
+ // quantity = ElectricResistivity.From(value, electricResistivityUnit);
+ // return true;
+ // case ElectricSurfaceChargeDensityUnit electricSurfaceChargeDensityUnit:
+ // quantity = ElectricSurfaceChargeDensity.From(value, electricSurfaceChargeDensityUnit);
+ // return true;
+ // case EnergyUnit energyUnit:
+ // quantity = Energy.From(value, energyUnit);
+ // return true;
+ // case EnergyDensityUnit energyDensityUnit:
+ // quantity = EnergyDensity.From(value, energyDensityUnit);
+ // return true;
+ // case EntropyUnit entropyUnit:
+ // quantity = Entropy.From(value, entropyUnit);
+ // return true;
+ // case ForceUnit forceUnit:
+ // quantity = Force.From(value, forceUnit);
+ // return true;
+ // case ForceChangeRateUnit forceChangeRateUnit:
+ // quantity = ForceChangeRate.From(value, forceChangeRateUnit);
+ // return true;
+ // case ForcePerLengthUnit forcePerLengthUnit:
+ // quantity = ForcePerLength.From(value, forcePerLengthUnit);
+ // return true;
+ // case FrequencyUnit frequencyUnit:
+ // quantity = Frequency.From(value, frequencyUnit);
+ // return true;
+ // case FuelEfficiencyUnit fuelEfficiencyUnit:
+ // quantity = FuelEfficiency.From(value, fuelEfficiencyUnit);
+ // return true;
+ // case HeatFluxUnit heatFluxUnit:
+ // quantity = HeatFlux.From(value, heatFluxUnit);
+ // return true;
+ // case HeatTransferCoefficientUnit heatTransferCoefficientUnit:
+ // quantity = HeatTransferCoefficient.From(value, heatTransferCoefficientUnit);
+ // return true;
+ // case IlluminanceUnit illuminanceUnit:
+ // quantity = Illuminance.From(value, illuminanceUnit);
+ // return true;
+ // case InformationUnit informationUnit:
+ // quantity = Information.From(value, informationUnit);
+ // return true;
+ // case IrradianceUnit irradianceUnit:
+ // quantity = Irradiance.From(value, irradianceUnit);
+ // return true;
+ // case IrradiationUnit irradiationUnit:
+ // quantity = Irradiation.From(value, irradiationUnit);
+ // return true;
+ // case JerkUnit jerkUnit:
+ // quantity = Jerk.From(value, jerkUnit);
+ // return true;
+ // case KinematicViscosityUnit kinematicViscosityUnit:
+ // quantity = KinematicViscosity.From(value, kinematicViscosityUnit);
+ // return true;
+ // case LengthUnit lengthUnit:
+ // quantity = Length.From(value, lengthUnit);
+ // return true;
+ // case LevelUnit levelUnit:
+ // quantity = Level.From(value, levelUnit);
+ // return true;
+ // case LinearDensityUnit linearDensityUnit:
+ // quantity = LinearDensity.From(value, linearDensityUnit);
+ // return true;
+ // case LinearPowerDensityUnit linearPowerDensityUnit:
+ // quantity = LinearPowerDensity.From(value, linearPowerDensityUnit);
+ // return true;
+ // case LuminanceUnit luminanceUnit:
+ // quantity = Luminance.From(value, luminanceUnit);
+ // return true;
+ // case LuminosityUnit luminosityUnit:
+ // quantity = Luminosity.From(value, luminosityUnit);
+ // return true;
+ // case LuminousFluxUnit luminousFluxUnit:
+ // quantity = LuminousFlux.From(value, luminousFluxUnit);
+ // return true;
+ // case LuminousIntensityUnit luminousIntensityUnit:
+ // quantity = LuminousIntensity.From(value, luminousIntensityUnit);
+ // return true;
+ // case MagneticFieldUnit magneticFieldUnit:
+ // quantity = MagneticField.From(value, magneticFieldUnit);
+ // return true;
+ // case MagneticFluxUnit magneticFluxUnit:
+ // quantity = MagneticFlux.From(value, magneticFluxUnit);
+ // return true;
+ // case MagnetizationUnit magnetizationUnit:
+ // quantity = Magnetization.From(value, magnetizationUnit);
+ // return true;
+ // case MassUnit massUnit:
+ // quantity = Mass.From(value, massUnit);
+ // return true;
+ // case MassConcentrationUnit massConcentrationUnit:
+ // quantity = MassConcentration.From(value, massConcentrationUnit);
+ // return true;
+ // case MassFlowUnit massFlowUnit:
+ // quantity = MassFlow.From(value, massFlowUnit);
+ // return true;
+ // case MassFluxUnit massFluxUnit:
+ // quantity = MassFlux.From(value, massFluxUnit);
+ // return true;
+ // case MassFractionUnit massFractionUnit:
+ // quantity = MassFraction.From(value, massFractionUnit);
+ // return true;
+ // case MassMomentOfInertiaUnit massMomentOfInertiaUnit:
+ // quantity = MassMomentOfInertia.From(value, massMomentOfInertiaUnit);
+ // return true;
+ // case MolarEnergyUnit molarEnergyUnit:
+ // quantity = MolarEnergy.From(value, molarEnergyUnit);
+ // return true;
+ // case MolarEntropyUnit molarEntropyUnit:
+ // quantity = MolarEntropy.From(value, molarEntropyUnit);
+ // return true;
+ // case MolarityUnit molarityUnit:
+ // quantity = Molarity.From(value, molarityUnit);
+ // return true;
+ // case MolarMassUnit molarMassUnit:
+ // quantity = MolarMass.From(value, molarMassUnit);
+ // return true;
+ // case PermeabilityUnit permeabilityUnit:
+ // quantity = Permeability.From(value, permeabilityUnit);
+ // return true;
+ // case PermittivityUnit permittivityUnit:
+ // quantity = Permittivity.From(value, permittivityUnit);
+ // return true;
+ // case PorousMediumPermeabilityUnit porousMediumPermeabilityUnit:
+ // quantity = PorousMediumPermeability.From(value, porousMediumPermeabilityUnit);
+ // return true;
+ // case PowerUnit powerUnit:
+ // quantity = Power.From(value, powerUnit);
+ // return true;
+ // case PowerDensityUnit powerDensityUnit:
+ // quantity = PowerDensity.From(value, powerDensityUnit);
+ // return true;
+ // case PowerRatioUnit powerRatioUnit:
+ // quantity = PowerRatio.From(value, powerRatioUnit);
+ // return true;
+ // case PressureUnit pressureUnit:
+ // quantity = Pressure.From(value, pressureUnit);
+ // return true;
+ // case PressureChangeRateUnit pressureChangeRateUnit:
+ // quantity = PressureChangeRate.From(value, pressureChangeRateUnit);
+ // return true;
+ // case RatioUnit ratioUnit:
+ // quantity = Ratio.From(value, ratioUnit);
+ // return true;
+ // case RatioChangeRateUnit ratioChangeRateUnit:
+ // quantity = RatioChangeRate.From(value, ratioChangeRateUnit);
+ // return true;
+ // case ReactiveEnergyUnit reactiveEnergyUnit:
+ // quantity = ReactiveEnergy.From(value, reactiveEnergyUnit);
+ // return true;
+ // case ReactivePowerUnit reactivePowerUnit:
+ // quantity = ReactivePower.From(value, reactivePowerUnit);
+ // return true;
+ // case ReciprocalAreaUnit reciprocalAreaUnit:
+ // quantity = ReciprocalArea.From(value, reciprocalAreaUnit);
+ // return true;
+ // case ReciprocalLengthUnit reciprocalLengthUnit:
+ // quantity = ReciprocalLength.From(value, reciprocalLengthUnit);
+ // return true;
+ // case RelativeHumidityUnit relativeHumidityUnit:
+ // quantity = RelativeHumidity.From(value, relativeHumidityUnit);
+ // return true;
+ // case RotationalAccelerationUnit rotationalAccelerationUnit:
+ // quantity = RotationalAcceleration.From(value, rotationalAccelerationUnit);
+ // return true;
+ // case RotationalSpeedUnit rotationalSpeedUnit:
+ // quantity = RotationalSpeed.From(value, rotationalSpeedUnit);
+ // return true;
+ // case RotationalStiffnessUnit rotationalStiffnessUnit:
+ // quantity = RotationalStiffness.From(value, rotationalStiffnessUnit);
+ // return true;
+ // case RotationalStiffnessPerLengthUnit rotationalStiffnessPerLengthUnit:
+ // quantity = RotationalStiffnessPerLength.From(value, rotationalStiffnessPerLengthUnit);
+ // return true;
+ // case ScalarUnit scalarUnit:
+ // quantity = Scalar.From(value, scalarUnit);
+ // return true;
+ // case SolidAngleUnit solidAngleUnit:
+ // quantity = SolidAngle.From(value, solidAngleUnit);
+ // return true;
+ // case SpecificEnergyUnit specificEnergyUnit:
+ // quantity = SpecificEnergy.From(value, specificEnergyUnit);
+ // return true;
+ // case SpecificEntropyUnit specificEntropyUnit:
+ // quantity = SpecificEntropy.From(value, specificEntropyUnit);
+ // return true;
+ // case SpecificFuelConsumptionUnit specificFuelConsumptionUnit:
+ // quantity = SpecificFuelConsumption.From(value, specificFuelConsumptionUnit);
+ // return true;
+ // case SpecificVolumeUnit specificVolumeUnit:
+ // quantity = SpecificVolume.From(value, specificVolumeUnit);
+ // return true;
+ // case SpecificWeightUnit specificWeightUnit:
+ // quantity = SpecificWeight.From(value, specificWeightUnit);
+ // return true;
+ // case SpeedUnit speedUnit:
+ // quantity = Speed.From(value, speedUnit);
+ // return true;
+ // case StandardVolumeFlowUnit standardVolumeFlowUnit:
+ // quantity = StandardVolumeFlow.From(value, standardVolumeFlowUnit);
+ // return true;
+ // case TemperatureUnit temperatureUnit:
+ // quantity = Temperature.From(value, temperatureUnit);
+ // return true;
+ // case TemperatureChangeRateUnit temperatureChangeRateUnit:
+ // quantity = TemperatureChangeRate.From(value, temperatureChangeRateUnit);
+ // return true;
+ // case TemperatureDeltaUnit temperatureDeltaUnit:
+ // quantity = TemperatureDelta.From(value, temperatureDeltaUnit);
+ // return true;
+ // case TemperatureGradientUnit temperatureGradientUnit:
+ // quantity = TemperatureGradient.From(value, temperatureGradientUnit);
+ // return true;
+ // case ThermalConductivityUnit thermalConductivityUnit:
+ // quantity = ThermalConductivity.From(value, thermalConductivityUnit);
+ // return true;
+ // case ThermalResistanceUnit thermalResistanceUnit:
+ // quantity = ThermalResistance.From(value, thermalResistanceUnit);
+ // return true;
+ // case TorqueUnit torqueUnit:
+ // quantity = Torque.From(value, torqueUnit);
+ // return true;
+ // case TorquePerLengthUnit torquePerLengthUnit:
+ // quantity = TorquePerLength.From(value, torquePerLengthUnit);
+ // return true;
+ // case TurbidityUnit turbidityUnit:
+ // quantity = Turbidity.From(value, turbidityUnit);
+ // return true;
+ // case VitaminAUnit vitaminAUnit:
+ // quantity = VitaminA.From(value, vitaminAUnit);
+ // return true;
+ // case VolumeUnit volumeUnit:
+ // quantity = Volume.From(value, volumeUnit);
+ // return true;
+ // case VolumeConcentrationUnit volumeConcentrationUnit:
+ // quantity = VolumeConcentration.From(value, volumeConcentrationUnit);
+ // return true;
+ // case VolumeFlowUnit volumeFlowUnit:
+ // quantity = VolumeFlow.From(value, volumeFlowUnit);
+ // return true;
+ // case VolumeFlowPerAreaUnit volumeFlowPerAreaUnit:
+ // quantity = VolumeFlowPerArea.From(value, volumeFlowPerAreaUnit);
+ // return true;
+ // case VolumePerLengthUnit volumePerLengthUnit:
+ // quantity = VolumePerLength.From(value, volumePerLengthUnit);
+ // return true;
+ // case VolumetricHeatCapacityUnit volumetricHeatCapacityUnit:
+ // quantity = VolumetricHeatCapacity.From(value, volumetricHeatCapacityUnit);
+ // return true;
+ // case WarpingMomentOfInertiaUnit warpingMomentOfInertiaUnit:
+ // quantity = WarpingMomentOfInertia.From(value, warpingMomentOfInertiaUnit);
+ // return true;
+ // default:
+ // {
+ // quantity = default(IQuantity);
+ // return false;
+ // }
+ // }
+ // }
+ //
+ // ///
+ // /// Try to dynamically parse a quantity string representation.
+ // ///
+ // /// The format provider to use for lookup. Defaults to if null.
+ // /// Type of quantity, such as .
+ // /// Quantity string representation, such as "1.5 kg". Must be compatible with given quantity type.
+ // /// The resulting quantity if successful, otherwise default.
+ // /// The parsed quantity.
+ // public static bool TryParse(IFormatProvider? formatProvider, Type quantityType, string quantityString, [NotNullWhen(true)] out IQuantity? quantity)
+ // {
+ // quantity = default(IQuantity);
+ //
+ // if (!typeof(IQuantity).IsAssignableFrom(quantityType))
+ // return false;
+ //
+ // var parser = QuantityParser.Default;
+ //
+ // return quantityType switch
+ // {
+ // Type _ when quantityType == typeof(Acceleration) => parser.TryParse(quantityString, formatProvider, Acceleration.From, out quantity),
+ // Type _ when quantityType == typeof(AmountOfSubstance) => parser.TryParse(quantityString, formatProvider, AmountOfSubstance.From, out quantity),
+ // Type _ when quantityType == typeof(AmplitudeRatio) => parser.TryParse(quantityString, formatProvider, AmplitudeRatio.From, out quantity),
+ // Type _ when quantityType == typeof(Angle) => parser.TryParse(quantityString, formatProvider, Angle.From, out quantity),
+ // Type _ when quantityType == typeof(ApparentEnergy) => parser.TryParse(quantityString, formatProvider, ApparentEnergy.From, out quantity),
+ // Type _ when quantityType == typeof(ApparentPower) => parser.TryParse(quantityString, formatProvider, ApparentPower.From, out quantity),
+ // Type _ when quantityType == typeof(Area) => parser.TryParse(quantityString, formatProvider, Area.From, out quantity),
+ // Type _ when quantityType == typeof(AreaDensity) => parser.TryParse(quantityString, formatProvider, AreaDensity.From, out quantity),
+ // Type _ when quantityType == typeof(AreaMomentOfInertia) => parser.TryParse(quantityString, formatProvider, AreaMomentOfInertia.From, out quantity),
+ // Type _ when quantityType == typeof(BitRate) => parser.TryParse(quantityString, formatProvider, BitRate.From, out quantity),
+ // Type _ when quantityType == typeof(BrakeSpecificFuelConsumption) => parser.TryParse(quantityString, formatProvider, BrakeSpecificFuelConsumption.From, out quantity),
+ // Type _ when quantityType == typeof(Capacitance) => parser.TryParse(quantityString, formatProvider, Capacitance.From, out quantity),
+ // Type _ when quantityType == typeof(CoefficientOfThermalExpansion) => parser.TryParse(quantityString, formatProvider, CoefficientOfThermalExpansion.From, out quantity),
+ // Type _ when quantityType == typeof(Compressibility) => parser.TryParse(quantityString, formatProvider, Compressibility.From, out quantity),
+ // Type _ when quantityType == typeof(Density) => parser.TryParse(quantityString, formatProvider, Density.From, out quantity),
+ // Type _ when quantityType == typeof(Duration) => parser.TryParse(quantityString, formatProvider, Duration.From, out quantity),
+ // Type _ when quantityType == typeof(DynamicViscosity) => parser.TryParse(quantityString, formatProvider, DynamicViscosity.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricAdmittance) => parser.TryParse(quantityString, formatProvider, ElectricAdmittance.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricCharge) => parser.TryParse(quantityString, formatProvider, ElectricCharge.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricChargeDensity) => parser.TryParse(quantityString, formatProvider, ElectricChargeDensity.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricConductance) => parser.TryParse(quantityString, formatProvider, ElectricConductance.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricConductivity) => parser.TryParse(quantityString, formatProvider, ElectricConductivity.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricCurrent) => parser.TryParse(quantityString, formatProvider, ElectricCurrent.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricCurrentDensity) => parser.TryParse(quantityString, formatProvider, ElectricCurrentDensity.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricCurrentGradient) => parser.TryParse(quantityString, formatProvider, ElectricCurrentGradient.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricField) => parser.TryParse(quantityString, formatProvider, ElectricField.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricInductance) => parser.TryParse(quantityString, formatProvider, ElectricInductance.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricPotential) => parser.TryParse(quantityString, formatProvider, ElectricPotential.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricPotentialAc) => parser.TryParse(quantityString, formatProvider, ElectricPotentialAc.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricPotentialChangeRate) => parser.TryParse(quantityString, formatProvider, ElectricPotentialChangeRate.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricPotentialDc) => parser.TryParse(quantityString, formatProvider, ElectricPotentialDc.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricResistance) => parser.TryParse(quantityString, formatProvider, ElectricResistance.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricResistivity) => parser.TryParse(quantityString, formatProvider, ElectricResistivity.From, out quantity),
+ // Type _ when quantityType == typeof(ElectricSurfaceChargeDensity) => parser.TryParse(quantityString, formatProvider, ElectricSurfaceChargeDensity.From, out quantity),
+ // Type _ when quantityType == typeof(Energy) => parser.TryParse(quantityString, formatProvider, Energy.From, out quantity),
+ // Type _ when quantityType == typeof(EnergyDensity) => parser.TryParse(quantityString, formatProvider, EnergyDensity.From, out quantity),
+ // Type _ when quantityType == typeof(Entropy) => parser.TryParse(quantityString, formatProvider, Entropy.From, out quantity),
+ // Type _ when quantityType == typeof(Force) => parser.TryParse(quantityString, formatProvider, Force.From, out quantity),
+ // Type _ when quantityType == typeof(ForceChangeRate) => parser.TryParse(quantityString, formatProvider, ForceChangeRate.From, out quantity),
+ // Type _ when quantityType == typeof(ForcePerLength) => parser.TryParse(quantityString, formatProvider, ForcePerLength.From, out quantity),
+ // Type _ when quantityType == typeof(Frequency) => parser.TryParse(quantityString, formatProvider, Frequency.From, out quantity),
+ // Type _ when quantityType == typeof(FuelEfficiency) => parser.TryParse(quantityString, formatProvider, FuelEfficiency.From, out quantity),
+ // Type _ when quantityType == typeof(HeatFlux) => parser.TryParse(quantityString, formatProvider, HeatFlux.From, out quantity),
+ // Type _ when quantityType == typeof(HeatTransferCoefficient) => parser.TryParse(quantityString, formatProvider, HeatTransferCoefficient.From, out quantity),
+ // Type _ when quantityType == typeof(Illuminance) => parser.TryParse(quantityString, formatProvider, Illuminance.From, out quantity),
+ // Type _ when quantityType == typeof(Information) => parser.TryParse(quantityString, formatProvider, Information.From, out quantity),
+ // Type _ when quantityType == typeof(Irradiance) => parser.TryParse(quantityString, formatProvider, Irradiance.From, out quantity),
+ // Type _ when quantityType == typeof(Irradiation) => parser.TryParse(quantityString, formatProvider, Irradiation.From, out quantity),
+ // Type _ when quantityType == typeof(Jerk) => parser.TryParse(quantityString, formatProvider, Jerk.From, out quantity),
+ // Type _ when quantityType == typeof(KinematicViscosity) => parser.TryParse(quantityString, formatProvider, KinematicViscosity.From, out quantity),
+ // Type _ when quantityType == typeof(Length) => parser.TryParse(quantityString, formatProvider, Length.From, out quantity),
+ // Type _ when quantityType == typeof(Level) => parser.TryParse(quantityString, formatProvider, Level.From, out quantity),
+ // Type _ when quantityType == typeof(LinearDensity) => parser.TryParse(quantityString, formatProvider, LinearDensity.From, out quantity),
+ // Type _ when quantityType == typeof(LinearPowerDensity) => parser.TryParse(quantityString, formatProvider, LinearPowerDensity.From, out quantity),
+ // Type _ when quantityType == typeof(Luminance) => parser.TryParse(quantityString, formatProvider, Luminance.From, out quantity),
+ // Type _ when quantityType == typeof(Luminosity) => parser.TryParse(quantityString, formatProvider, Luminosity.From, out quantity),
+ // Type _ when quantityType == typeof(LuminousFlux) => parser.TryParse(quantityString, formatProvider, LuminousFlux.From, out quantity),
+ // Type _ when quantityType == typeof(LuminousIntensity) => parser.TryParse(quantityString, formatProvider, LuminousIntensity.From, out quantity),
+ // Type _ when quantityType == typeof(MagneticField) => parser.TryParse(quantityString, formatProvider, MagneticField.From, out quantity),
+ // Type _ when quantityType == typeof(MagneticFlux) => parser.TryParse(quantityString, formatProvider, MagneticFlux.From, out quantity),
+ // Type _ when quantityType == typeof(Magnetization) => parser.TryParse(quantityString, formatProvider, Magnetization.From, out quantity),
+ // Type _ when quantityType == typeof(Mass) => parser.TryParse(quantityString, formatProvider, Mass.From, out quantity),
+ // Type _ when quantityType == typeof(MassConcentration) => parser.TryParse(quantityString, formatProvider, MassConcentration.From, out quantity),
+ // Type _ when quantityType == typeof(MassFlow) => parser.TryParse(quantityString, formatProvider, MassFlow.From, out quantity),
+ // Type _ when quantityType == typeof(MassFlux) => parser.TryParse(quantityString, formatProvider, MassFlux.From, out quantity),
+ // Type _ when quantityType == typeof(MassFraction) => parser.TryParse(quantityString, formatProvider, MassFraction.From, out quantity),
+ // Type _ when quantityType == typeof(MassMomentOfInertia) => parser.TryParse(quantityString, formatProvider, MassMomentOfInertia.From, out quantity),
+ // Type _ when quantityType == typeof(MolarEnergy) => parser.TryParse(quantityString, formatProvider, MolarEnergy.From, out quantity),
+ // Type _ when quantityType == typeof(MolarEntropy) => parser.TryParse(quantityString, formatProvider, MolarEntropy.From, out quantity),
+ // Type _ when quantityType == typeof(Molarity) => parser.TryParse(quantityString, formatProvider, Molarity.From, out quantity),
+ // Type _ when quantityType == typeof(MolarMass) => parser.TryParse(quantityString, formatProvider, MolarMass.From, out quantity),
+ // Type _ when quantityType == typeof(Permeability) => parser.TryParse(quantityString, formatProvider, Permeability.From, out quantity),
+ // Type _ when quantityType == typeof(Permittivity) => parser.TryParse(quantityString, formatProvider, Permittivity.From, out quantity),
+ // Type _ when quantityType == typeof(PorousMediumPermeability) => parser.TryParse(quantityString, formatProvider, PorousMediumPermeability.From, out quantity),
+ // Type _ when quantityType == typeof(Power) => parser.TryParse(quantityString, formatProvider, Power.From, out quantity),
+ // Type _ when quantityType == typeof(PowerDensity) => parser.TryParse(quantityString, formatProvider, PowerDensity.From, out quantity),
+ // Type _ when quantityType == typeof(PowerRatio) => parser.TryParse(quantityString, formatProvider, PowerRatio.From, out quantity),
+ // Type _ when quantityType == typeof(Pressure) => parser.TryParse(quantityString, formatProvider, Pressure.From, out quantity),
+ // Type _ when quantityType == typeof(PressureChangeRate) => parser.TryParse(quantityString, formatProvider, PressureChangeRate.From, out quantity),
+ // Type _ when quantityType == typeof(Ratio) => parser.TryParse(quantityString, formatProvider, Ratio.From, out quantity),
+ // Type _ when quantityType == typeof(RatioChangeRate) => parser.TryParse(quantityString, formatProvider, RatioChangeRate.From, out quantity),
+ // Type _ when quantityType == typeof(ReactiveEnergy) => parser.TryParse(quantityString, formatProvider, ReactiveEnergy.From, out quantity),
+ // Type _ when quantityType == typeof(ReactivePower) => parser.TryParse(quantityString, formatProvider, ReactivePower.From, out quantity),
+ // Type _ when quantityType == typeof(ReciprocalArea) => parser.TryParse(quantityString, formatProvider, ReciprocalArea.From, out quantity),
+ // Type _ when quantityType == typeof(ReciprocalLength) => parser.TryParse(quantityString, formatProvider, ReciprocalLength.From, out quantity),
+ // Type _ when quantityType == typeof(RelativeHumidity) => parser.TryParse(quantityString, formatProvider, RelativeHumidity.From, out quantity),
+ // Type _ when quantityType == typeof(RotationalAcceleration) => parser.TryParse(quantityString, formatProvider, RotationalAcceleration.From, out quantity),
+ // Type _ when quantityType == typeof(RotationalSpeed) => parser.TryParse(quantityString, formatProvider, RotationalSpeed.From, out quantity),
+ // Type _ when quantityType == typeof(RotationalStiffness) => parser.TryParse(quantityString, formatProvider, RotationalStiffness.From, out quantity),
+ // Type _ when quantityType == typeof(RotationalStiffnessPerLength) => parser.TryParse(quantityString, formatProvider, RotationalStiffnessPerLength.From, out quantity),
+ // Type _ when quantityType == typeof(Scalar) => parser.TryParse(quantityString, formatProvider, Scalar.From, out quantity),
+ // Type _ when quantityType == typeof(SolidAngle) => parser.TryParse(quantityString, formatProvider, SolidAngle.From, out quantity),
+ // Type _ when quantityType == typeof(SpecificEnergy) => parser.TryParse(quantityString, formatProvider, SpecificEnergy.From, out quantity),
+ // Type _ when quantityType == typeof(SpecificEntropy) => parser.TryParse(quantityString, formatProvider, SpecificEntropy.From, out quantity),
+ // Type _ when quantityType == typeof(SpecificFuelConsumption) => parser.TryParse(quantityString, formatProvider, SpecificFuelConsumption.From, out quantity),
+ // Type _ when quantityType == typeof(SpecificVolume) => parser.TryParse(quantityString, formatProvider, SpecificVolume.From, out quantity),
+ // Type _ when quantityType == typeof(SpecificWeight) => parser.TryParse(quantityString, formatProvider, SpecificWeight.From, out quantity),
+ // Type _ when quantityType == typeof(Speed) => parser.TryParse(quantityString, formatProvider, Speed.From, out quantity),
+ // Type _ when quantityType == typeof(StandardVolumeFlow) => parser.TryParse(quantityString, formatProvider, StandardVolumeFlow.From, out quantity),
+ // Type _ when quantityType == typeof(Temperature) => parser.TryParse(quantityString, formatProvider, Temperature.From, out quantity),
+ // Type _ when quantityType == typeof(TemperatureChangeRate) => parser.TryParse(quantityString, formatProvider, TemperatureChangeRate.From, out quantity),
+ // Type _ when quantityType == typeof(TemperatureDelta) => parser.TryParse(quantityString, formatProvider, TemperatureDelta.From, out quantity),
+ // Type _ when quantityType == typeof(TemperatureGradient) => parser.TryParse(quantityString, formatProvider, TemperatureGradient.From, out quantity),
+ // Type _ when quantityType == typeof(ThermalConductivity) => parser.TryParse(quantityString, formatProvider, ThermalConductivity.From, out quantity),
+ // Type _ when quantityType == typeof(ThermalResistance) => parser.TryParse(quantityString, formatProvider, ThermalResistance.From, out quantity),
+ // Type _ when quantityType == typeof(Torque) => parser.TryParse(quantityString, formatProvider, Torque.From, out quantity),
+ // Type _ when quantityType == typeof(TorquePerLength) => parser.TryParse(quantityString, formatProvider, TorquePerLength.From, out quantity),
+ // Type _ when quantityType == typeof(Turbidity) => parser.TryParse(quantityString, formatProvider, Turbidity.From, out quantity),
+ // Type _ when quantityType == typeof(VitaminA) => parser.TryParse(quantityString, formatProvider, VitaminA.From, out quantity),
+ // Type _ when quantityType == typeof(Volume) => parser.TryParse(quantityString, formatProvider, Volume.From, out quantity),
+ // Type _ when quantityType == typeof(VolumeConcentration) => parser.TryParse(quantityString, formatProvider, VolumeConcentration.From, out quantity),
+ // Type _ when quantityType == typeof(VolumeFlow) => parser.TryParse(quantityString, formatProvider, VolumeFlow.From, out quantity),
+ // Type _ when quantityType == typeof(VolumeFlowPerArea) => parser.TryParse(quantityString, formatProvider, VolumeFlowPerArea.From, out quantity),
+ // Type _ when quantityType == typeof(VolumePerLength) => parser.TryParse