Skip to content

Commit

Permalink
Update the ToString format of the BaseUnits/Dimensions (#1486)
Browse files Browse the repository at this point in the history
- `BaseUnits`: no longer using the `AbbeviationsCache`, the new format
is `L=Meter, M=Kilogram, T=Second`
- `BaseDimensions`: the exponent moved inside the dimension-brackets:
`[Length][Time^-1]`
- `BaseDimensions`: minor performance improvements

As mentioned in #1452, the main motivation here is the removal of the
potential side effects of accessing/loading the unit abbreviations (e.g.
during the `QuantityInfo` construction)
  • Loading branch information
lipchev authored Jan 2, 2025
1 parent 1f90e8f commit 9090623
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 47 deletions.
7 changes: 4 additions & 3 deletions UnitsNet.Tests/BaseDimensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void IsBaseQuantity_ForAcceleration_ReturnsFalse()
{
Assert.False(Acceleration.BaseDimensions.IsBaseQuantity());
}

[Theory]
[InlineData(2, 0, 0, 0, 0, 0, 0)]
[InlineData(0, 2, 0, 0, 0, 0, 0)]
Expand Down Expand Up @@ -733,13 +733,13 @@ public void CheckBaseDimensionMultiplicationWithForceEqualsMassTimesAcceleration
[Fact]
public void CheckToStringUsingMolarEntropy()
{
Assert.Equal("[Length]^2[Mass][Time]^-2[Temperature]^-1[Amount]^-1", MolarEntropy.BaseDimensions.ToString());
Assert.Equal("[Length^2][Mass][Time^-2][Temperature^-1][Amount^-1]", MolarEntropy.BaseDimensions.ToString());
}

[Fact]
public void CheckToStringUsingSpeed()
{
Assert.Equal("[Length][Time]^-1", Speed.BaseDimensions.ToString());
Assert.Equal("[Length][Time^-1]", Speed.BaseDimensions.ToString());
}

[Fact]
Expand Down Expand Up @@ -782,5 +782,6 @@ public void IsDimensionlessMethodImplementedCorrectly()
// Example case
Assert.True(Level.BaseDimensions.IsDimensionless());
}

}
}
25 changes: 15 additions & 10 deletions UnitsNet.Tests/BaseUnitsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,21 @@ public void ToStringGivesExpectedResult()
AmountOfSubstanceUnit.Mole,
LuminousIntensityUnit.Candela);

UnitAbbreviationsCache cache = UnitsNetSetup.Default.UnitAbbreviations;
var m = cache.GetDefaultAbbreviation(LengthUnit.Meter);
var kg = cache.GetDefaultAbbreviation(MassUnit.Kilogram);
var s = cache.GetDefaultAbbreviation(DurationUnit.Second);
var A = cache.GetDefaultAbbreviation(ElectricCurrentUnit.Ampere);
var K = cache.GetDefaultAbbreviation(TemperatureUnit.Kelvin);
var mol = cache.GetDefaultAbbreviation(AmountOfSubstanceUnit.Mole);
var cd = cache.GetDefaultAbbreviation(LuminousIntensityUnit.Candela);

Assert.Equal($"[Length]: {m}, [Mass]: {kg}, [Time]: {s}, [Current]: {A}, [Temperature]: {K}, [Amount]: {mol}, [LuminousIntensity]: {cd}", siBaseUnits.ToString());
Assert.Equal("L=Meter, M=Kilogram, T=Second, I=Ampere, Θ=Kelvin, N=Mole, J=Candela", siBaseUnits.ToString());
}

[Fact]
public void ToString_WithFewerDimensions_ContainsOnlyTheSpecifiedDimensions()
{
var siBaseUnits = new BaseUnits(length:LengthUnit.Meter, mass:MassUnit.Gram, time:DurationUnit.Second, temperature:TemperatureUnit.DegreeCelsius);

Assert.Equal("L=Meter, M=Gram, T=Second, Θ=DegreeCelsius", siBaseUnits.ToString());
}

[Fact]
public void ToString_WithUndefinedUnits_ReturnsUndefined()
{
Assert.Equal("Undefined", BaseUnits.Undefined.ToString());
}

[Fact]
Expand Down
31 changes: 21 additions & 10 deletions UnitsNet/BaseDimensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Text;
using System.Linq;

namespace UnitsNet
{
Expand All @@ -30,9 +29,13 @@ public BaseDimensions(int length, int mass, int time, int current, int temperatu
/// <returns>True if the dimensions represent a base quantity, otherwise false.</returns>
public bool IsBaseQuantity()
{
var dimensionsArray = new[] { Length, Mass, Time, Current, Temperature, Amount, LuminousIntensity };
bool onlyOneEqualsOne = dimensionsArray.Select(dimension => dimension is 0 or 1 ? dimension : 2).Sum() == 1;
return onlyOneEqualsOne;
return (Length == 1 && Mass == 0 && Time == 0 && Current == 0 && Temperature == 0 && Amount == 0 && LuminousIntensity == 0) ||
(Length == 0 && Mass == 1 && Time == 0 && Current == 0 && Temperature == 0 && Amount == 0 && LuminousIntensity == 0) ||
(Length == 0 && Mass == 0 && Time == 1 && Current == 0 && Temperature == 0 && Amount == 0 && LuminousIntensity == 0) ||
(Length == 0 && Mass == 0 && Time == 0 && Current == 1 && Temperature == 0 && Amount == 0 && LuminousIntensity == 0) ||
(Length == 0 && Mass == 0 && Time == 0 && Current == 0 && Temperature == 1 && Amount == 0 && LuminousIntensity == 0) ||
(Length == 0 && Mass == 0 && Time == 0 && Current == 0 && Temperature == 0 && Amount == 1 && LuminousIntensity == 0) ||
(Length == 0 && Mass == 0 && Time == 0 && Current == 0 && Temperature == 0 && Amount == 0 && LuminousIntensity == 1);
}

/// <summary>
Expand Down Expand Up @@ -71,7 +74,11 @@ public override bool Equals(object? obj)
/// <inheritdoc />
public override int GetHashCode()
{
return new {Length, Mass, Time, Current, Temperature, Amount, LuminousIntensity}.GetHashCode();
#if NET
return HashCode.Combine(Length, Mass, Time, Current, Temperature, Amount, LuminousIntensity);
#else
return new { Length, Mass, Time, Current, Temperature, Amount, LuminousIntensity }.GetHashCode();
#endif
}

/// <summary>
Expand Down Expand Up @@ -182,12 +189,16 @@ public override string ToString()

private static void AppendDimensionString(StringBuilder sb, string name, int value)
{
if (0 != value)
switch (value)
{
sb.AppendFormat("[{0}]", name);

if (1 != value)
sb.AppendFormat("^{0}", value);
case 0:
return;
case 1:
sb.AppendFormat("[{0}]", name);
break;
default:
sb.AppendFormat("[{0}^{1}]", name, value);
break;
}
}

Expand Down
64 changes: 40 additions & 24 deletions UnitsNet/BaseUnits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System;
using System.Text;
using System.Collections.Generic;
using UnitsNet.Units;

namespace UnitsNet
Expand Down Expand Up @@ -103,11 +103,11 @@ public bool IsSubsetOf(BaseUnits other)
/// <inheritdoc />
public override int GetHashCode()
{
#if NET
#if NET
return HashCode.Combine(Length, Mass, Time, Current, Temperature, Amount, LuminousIntensity);
#else
#else
return new {Length, Mass, Time, Current, Temperature, Amount, LuminousIntensity}.GetHashCode();
#endif
#endif
}

/// <summary>
Expand All @@ -133,31 +133,47 @@ public override int GetHashCode()
{
return !(left == right);
}

/// <inheritdoc />
public override string ToString()
{
if(!Equals(Undefined))
if (Equals(Undefined))
{
var sb = new StringBuilder();

string GetDefaultAbbreviation<TUnitType>(TUnitType? unitOrNull) where TUnitType : struct, Enum => unitOrNull is { } unit
? UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(unit)
: "N/A";
return "Undefined";
}

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 string.Join(", ", GetUnitsDefined());

return sb.ToString();
}
else
IEnumerable<string> GetUnitsDefined()
{
return "Undefined";
if (Length is not null)
{
yield return $"L={Length}";
}
if (Mass is not null)
{
yield return $"M={Mass}";
}
if (Time is not null)
{
yield return $"T={Time}";
}
if (Current is not null)
{
yield return $"I={Current}";
}
if (Temperature is not null)
{
yield return $"Θ={Temperature}";
}
if (Amount is not null)
{
yield return $"N={Amount}";
}
if (LuminousIntensity is not null)
{
yield return $"J={LuminousIntensity}";
}
}
}

Expand Down Expand Up @@ -200,8 +216,8 @@ string GetDefaultAbbreviation<TUnitType>(TUnitType? unitOrNull) where TUnitType
/// Gets a value indicating whether all base units are defined.
/// </summary>
/// <remarks>
/// This property returns <c>true</c> if all seven base units
/// (Length, Mass, Time, Current, Temperature, Amount, and LuminousIntensity)
/// This property returns <c>true</c> if all seven base units
/// (Length, Mass, Time, Current, Temperature, Amount, and LuminousIntensity)
/// are non-null; otherwise, it returns <c>false</c>.
/// </remarks>
public bool IsFullyDefined => Length is not null &&
Expand Down

0 comments on commit 9090623

Please sign in to comment.