Skip to content

Commit 5f13d99

Browse files
authored
✨Add UnitsNetSetup to hold global static state (#1267)
Added `UnitsNetSetup` to gather global state in a single place as a singleton, with the possibility of passing instances of it to static methods later to override the defaults. This way, state is no longer scattered among multiple classes that depend on each other in non-obvious ways and where initialization order has caused issues up until now. ### Changes - Add `UnitsNetSetup` to hold global state as a singleton property `Default` that controls default state initialization. - Add properties for all "services" that depend on global state: `UnitConverter, UnitAbbreviationsCache, UnitParser, QuantityParser` - Forward all other `Default` singleton properties from these services to `UnitsNetSetup.Default` and mark obsolete - Add some TODOs where it seems functionality is missing ### Testing ❌ Still running into a handful of flaky tests due to racing conditions. This was a regression in #1210, but still not fixed. Will investigate and fix in separate PR. ``` UnitAbbreviationsCacheTests.AllUnitsImplementToStringForInvariantCulture UnitAbbreviationsCacheTests.MapUnitToAbbreviation_AddCustomUnit_DoesNotOverrideDefaultAbbreviationForAlreadyMappedUnits ```
1 parent 49f8864 commit 5f13d99

13 files changed

+440
-170
lines changed

UnitsNet.Serialization.JsonNet/AbbreviatedUnitsConverter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public AbbreviatedUnitsConverter()
4545
/// </summary>
4646
/// <param name="comparer">The comparer used to compare the property/quantity names (e.g. StringComparer.OrdinalIgnoreCase) </param>
4747
public AbbreviatedUnitsConverter(IEqualityComparer<string?> comparer)
48-
: this(new Dictionary<string, QuantityInfo>(Quantity.ByName, comparer), UnitAbbreviationsCache.Default, comparer)
48+
: this(new Dictionary<string, QuantityInfo>(Quantity.ByName, comparer), UnitsNetSetup.Default.UnitAbbreviations, comparer)
4949
{
5050
}
5151

UnitsNet.Tests/AlphabeticalOrderer.cs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Licensed under MIT No Attribution, see LICENSE file at the root.
2+
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.
3+
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using Xunit.Abstractions;
7+
using Xunit.Sdk;
8+
9+
namespace UnitsNet.Tests;
10+
11+
/// <summary>
12+
/// Useful for debugging tests where a particular order of tests is required.
13+
/// </summary>
14+
/// <example>
15+
/// Add the attribute to your test class:
16+
/// <code>
17+
/// <![CDATA[
18+
/// [TestCaseOrderer(
19+
/// ordererTypeName: "UnitsNet.Tests.AlphabeticalOrderer",
20+
/// ordererAssemblyName: "UnitsNet.Tests")]
21+
/// ]]>
22+
/// </code>
23+
/// </example>
24+
public class AlphabeticalOrderer : ITestCaseOrderer
25+
{
26+
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(
27+
IEnumerable<TTestCase> testCases) where TTestCase : ITestCase =>
28+
testCases.OrderBy(testCase => testCase.TestMethod.Method.Name);
29+
}

UnitsNet.Tests/BaseUnitsTests.cs

+8-7
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,14 @@ public void ToStringGivesExpectedResult()
140140
AmountOfSubstanceUnit.Mole,
141141
LuminousIntensityUnit.Candela);
142142

143-
var m = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(LengthUnit.Meter);
144-
var kg = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(MassUnit.Kilogram);
145-
var s = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(DurationUnit.Second);
146-
var A = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(ElectricCurrentUnit.Ampere);
147-
var K = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(TemperatureUnit.Kelvin);
148-
var mol = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(AmountOfSubstanceUnit.Mole);
149-
var cd = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(LuminousIntensityUnit.Candela);
143+
UnitAbbreviationsCache cache = UnitsNetSetup.Default.UnitAbbreviations;
144+
var m = cache.GetDefaultAbbreviation(LengthUnit.Meter);
145+
var kg = cache.GetDefaultAbbreviation(MassUnit.Kilogram);
146+
var s = cache.GetDefaultAbbreviation(DurationUnit.Second);
147+
var A = cache.GetDefaultAbbreviation(ElectricCurrentUnit.Ampere);
148+
var K = cache.GetDefaultAbbreviation(TemperatureUnit.Kelvin);
149+
var mol = cache.GetDefaultAbbreviation(AmountOfSubstanceUnit.Mole);
150+
var cd = cache.GetDefaultAbbreviation(LuminousIntensityUnit.Candela);
150151

151152
Assert.Equal($"[Length]: {m}, [Mass]: {kg}, [Time]: {s}, [Current]: {A}, [Temperature]: {K}, [Amount]: {mol}, [LuminousIntensity]: {cd}", siBaseUnits.ToString());
152153
}

UnitsNet.Tests/QuantityIFormattableTests.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ public void EmptyOrNullFormatStringEqualsGFormat()
2727
[Fact]
2828
public void AFormatGetsAbbreviations()
2929
{
30-
Assert.Equal(UnitAbbreviationsCache.Default.GetDefaultAbbreviation(MyLength.Unit, CultureInfo.InvariantCulture), MyLength.ToString("a", CultureInfo.InvariantCulture));
31-
Assert.Equal(UnitAbbreviationsCache.Default.GetDefaultAbbreviation(MyLength.Unit, CultureInfo.InvariantCulture), MyLength.ToString("a0", CultureInfo.InvariantCulture));
30+
UnitAbbreviationsCache cache = UnitsNetSetup.Default.UnitAbbreviations;
31+
Assert.Equal(cache.GetDefaultAbbreviation(MyLength.Unit, CultureInfo.InvariantCulture), MyLength.ToString("a", CultureInfo.InvariantCulture));
32+
Assert.Equal(cache.GetDefaultAbbreviation(MyLength.Unit, CultureInfo.InvariantCulture), MyLength.ToString("a0", CultureInfo.InvariantCulture));
3233

33-
Assert.Equal(UnitAbbreviationsCache.Default.GetUnitAbbreviations(MyLength.Unit, CultureInfo.InvariantCulture)[1], MyLength.ToString("a1", CultureInfo.InvariantCulture));
34-
Assert.Equal(UnitAbbreviationsCache.Default.GetUnitAbbreviations(MyLength.Unit, CultureInfo.InvariantCulture)[2], MyLength.ToString("a2", CultureInfo.InvariantCulture));
34+
Assert.Equal(cache.GetUnitAbbreviations(MyLength.Unit, CultureInfo.InvariantCulture)[1], MyLength.ToString("a1", CultureInfo.InvariantCulture));
35+
Assert.Equal(cache.GetUnitAbbreviations(MyLength.Unit, CultureInfo.InvariantCulture)[2], MyLength.ToString("a2", CultureInfo.InvariantCulture));
3536
}
3637

3738
[Fact]

UnitsNet.Tests/UnitAbbreviationsCacheTests.cs

+18
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,24 @@ public void GetDefaultAbbreviationFallsBackToUsEnglishCulture()
229229
Assert.Equal("US english abbreviation for Unit1", abbreviation);
230230
}
231231

232+
[Fact]
233+
public void MapUnitToAbbreviation_DoesNotAffectOtherCacheInstances()
234+
{
235+
var culture = AmericanCulture;
236+
var unit = AreaUnit.SquareMeter;
237+
238+
var cache1 = new UnitAbbreviationsCache();
239+
cache1.MapUnitToAbbreviation(unit, culture, "m^2");
240+
241+
var cache2 = new UnitAbbreviationsCache();
242+
cache2.MapUnitToAbbreviation(unit, culture, "m2");
243+
244+
Assert.Equal(new[] { "m²", "m^2" }, cache1.GetUnitAbbreviations(unit, culture));
245+
Assert.Equal(new[] { "m²", "m2" }, cache2.GetUnitAbbreviations(unit, culture));
246+
Assert.Equal("m²", cache1.GetDefaultAbbreviation(unit, culture));
247+
Assert.Equal("m²", cache2.GetDefaultAbbreviation(unit, culture));
248+
}
249+
232250
[Fact]
233251
public void MapUnitToAbbreviation_AddCustomUnit_DoesNotOverrideDefaultAbbreviationForAlreadyMappedUnits()
234252
{

UnitsNet/CustomCode/Quantity.cs

+1-9
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,7 @@ namespace UnitsNet
99
{
1010
public partial class Quantity
1111
{
12-
static Quantity()
13-
{
14-
Default = new QuantityInfoLookup();
15-
}
16-
17-
private static QuantityInfoLookup Default
18-
{
19-
get;
20-
}
12+
private static QuantityInfoLookup Default => UnitsNetSetup.Default.QuantityInfoLookup;
2113

2214
/// <summary>
2315
/// All enum value names of <see cref="Infos"/>, such as "Length" and "Mass".

UnitsNet/CustomCode/QuantityParser.cs

+2-6
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public class QuantityParser
3737
/// <summary>
3838
/// The default instance of <see cref="QuantityParser"/>, which uses <see cref="UnitAbbreviationsCache.Default"/> unit abbreviations.
3939
/// </summary>
40-
public static QuantityParser Default { get; }
40+
[Obsolete("Use UnitsNetSetup.Default.QuantityParser instead.")]
41+
public static QuantityParser Default => UnitsNetSetup.Default.QuantityParser;
4142

4243
/// <summary>
4344
/// Creates an instance of <see cref="QuantityParser"/>, optionally specifying an <see cref="UnitAbbreviationsCache"/>
@@ -50,11 +51,6 @@ public QuantityParser(UnitAbbreviationsCache? unitAbbreviationsCache = null)
5051
_unitParser = new UnitParser(_unitAbbreviationsCache);
5152
}
5253

53-
static QuantityParser()
54-
{
55-
Default = new QuantityParser(UnitAbbreviationsCache.Default);
56-
}
57-
5854
/// <summary>
5955
/// Parses a quantity from a string, such as "1.2 kg" to <see cref="Length"/> or "100 cm" to <see cref="Mass"/>.
6056
/// </summary>

0 commit comments

Comments
 (0)