Skip to content

Commit 48ac5df

Browse files
lipchevangularsen
andauthored
QuantityInfo: internalizing the UnitInfo construction (#1555)
- `QuantityInfo`: internalizing the `UnitInfo` construction - `QuantityInfo`: introducing a delegate for constructing the quantity (required only for net standard) - `QuantityInfo`: introducing an optional `ResourceDictionary` - `QuantityInfo`: replacing the `TUnit[]` with an `IReadOnlyCollection<TUnit>` - `UnitInfo`: introducing a back-reference to the `QuantityInfo` (making the `QuantityName` `[Obsolete]`) - `IQuantity`: added the `QuantityInfo<TQuantity, TUnit>`, the `From(double, TUnit)` method and default implementations for the non-generic properties - `QuantityInfoLookup`: added another collection for the quantity by type mapping (replacing the generated code in `Quantity.g.s`). - `UnitAbbreviationsCache`: `ReadAbbreviationsFromResourceFile` implemented using the provided `ResourceManager` (if available) - updating the `QuantityInfo` definitions for all quantities (introducing a concrete class, such as `MassInfo`) with helpers for creating a derived configuration - `HowMuch` upgraded to `IQuantity<HowMuch, HowMuchUnit>` (the original `QuantityInfo` is now abstract) - `Quantity` refactored the `Parse` / `From*` methods using the default `QuantityParser` / `QuantityInfoLookup` - `Quantity` replaced the `ByName` dictionary with an `IReadOnlyDictionary` - `UnitsNet.csproj` / `UnitsNet.Tests.csproj`: added some (specific) implicit usings --------- Co-authored-by: Andreas Gullberg Larsen <[email protected]>
1 parent c95ec58 commit 48ac5df

File tree

175 files changed

+13532
-5928
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

175 files changed

+13532
-5928
lines changed

CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs

Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using CodeGen.Helpers;
77
using CodeGen.JsonTypes;
8+
using static System.Runtime.InteropServices.JavaScript.JSType;
89

910
namespace CodeGen.Generators.UnitsNetGen
1011
{
@@ -34,13 +35,10 @@ public string Generate()
3435
{
3536
Writer.WL(GeneratedFileHeader);
3637
Writer.WL(@"
37-
using System;
38-
using System.Diagnostics;
39-
using System.Diagnostics.CodeAnalysis;
38+
4039
using System.Globalization;
41-
using System.Linq;
40+
using System.Resources;
4241
using System.Runtime.Serialization;
43-
using UnitsNet.Units;
4442
#if NET
4543
using System.Numerics;
4644
#endif
@@ -122,6 +120,7 @@ namespace UnitsNet
122120
[DataMember(Name = ""Unit"", Order = 2)]
123121
private readonly {_unitEnumName}? _unit;
124122
");
123+
GenerateQuantityInfo();
125124
GenerateStaticConstructor();
126125
GenerateInstanceConstructors();
127126
GenerateStaticProperties();
@@ -141,36 +140,86 @@ namespace UnitsNet
141140
}}");
142141
return Writer.ToString();
143142
}
144-
145-
private void GenerateStaticConstructor()
143+
144+
private void GenerateQuantityInfo()
146145
{
146+
var quantityInfoClassName = $"{_quantity.Name}Info";
147147
BaseDimensions baseDimensions = _quantity.BaseDimensions;
148+
var createDimensionsExpression = _isDimensionless
149+
? "BaseDimensions.Dimensionless"
150+
: $"new BaseDimensions({baseDimensions.L}, {baseDimensions.M}, {baseDimensions.T}, {baseDimensions.I}, {baseDimensions.Θ}, {baseDimensions.N}, {baseDimensions.J})";
151+
148152
Writer.WL($@"
149-
static {_quantity.Name}()
153+
/// <summary>
154+
/// Provides detailed information about the <see cref=""{_quantity.Name}""/> quantity, including its name, base unit, unit mappings, base dimensions, and conversion functions.
155+
/// </summary>
156+
public sealed class {quantityInfoClassName}: QuantityInfo<{_quantity.Name}, {_unitEnumName}>
150157
{{");
151-
Writer.WL(_isDimensionless ? $@"
152-
BaseDimensions = BaseDimensions.Dimensionless;" : $@"
153-
BaseDimensions = new BaseDimensions({baseDimensions.L}, {baseDimensions.M}, {baseDimensions.T}, {baseDimensions.I}, {baseDimensions.Θ}, {baseDimensions.N}, {baseDimensions.J});");
154-
155158
Writer.WL($@"
156-
BaseUnit = {_unitEnumName}.{_quantity.BaseUnit};
157-
Units = Enum.GetValues(typeof({_unitEnumName})).Cast<{_unitEnumName}>().ToArray();
158-
Zero = new {_quantity.Name}(0, BaseUnit);
159-
Info = new QuantityInfo<{_unitEnumName}>(""{_quantity.Name}"",
160-
new UnitInfo<{_unitEnumName}>[]
161-
{{");
159+
/// <inheritdoc />
160+
public {quantityInfoClassName}(string name, {_unitEnumName} baseUnit, IEnumerable<IUnitDefinition<{_unitEnumName}>> unitMappings, {_quantity.Name} zero, BaseDimensions baseDimensions,
161+
QuantityFromDelegate<{_quantity.Name}, {_unitEnumName}> fromDelegate, ResourceManager? unitAbbreviations)
162+
: base(name, baseUnit, unitMappings, zero, baseDimensions, fromDelegate, unitAbbreviations)
163+
{{
164+
}}
165+
166+
/// <inheritdoc />
167+
public {quantityInfoClassName}(string name, {_unitEnumName} baseUnit, IEnumerable<IUnitDefinition<{_unitEnumName}>> unitMappings, {_quantity.Name} zero, BaseDimensions baseDimensions)
168+
: this(name, baseUnit, unitMappings, zero, baseDimensions, {_quantity.Name}.From, new ResourceManager(""UnitsNet.GeneratedCode.Resources.{_quantity.Name}"", typeof({_quantity.Name}).Assembly))
169+
{{
170+
}}
171+
172+
/// <summary>
173+
/// Creates a new instance of the <see cref=""{quantityInfoClassName}""/> class with the default settings for the {_quantity.Name} quantity.
174+
/// </summary>
175+
/// <returns>A new instance of the <see cref=""{quantityInfoClassName}""/> class with the default settings.</returns>
176+
public static {quantityInfoClassName} CreateDefault()
177+
{{
178+
return new {quantityInfoClassName}(nameof({_quantity.Name}), DefaultBaseUnit, GetDefaultMappings(), new {_quantity.Name}(0, DefaultBaseUnit), DefaultBaseDimensions);
179+
}}
180+
181+
/// <summary>
182+
/// Creates a new instance of the <see cref=""{quantityInfoClassName}""/> class with the default settings for the {_quantity.Name} quantity and a callback for customizing the default unit mappings.
183+
/// </summary>
184+
/// <param name=""customizeUnits"">
185+
/// A callback function for customizing the default unit mappings.
186+
/// </param>
187+
/// <returns>
188+
/// A new instance of the <see cref=""{quantityInfoClassName}""/> class with the default settings.
189+
/// </returns>
190+
public static {quantityInfoClassName} CreateDefault(Func<IEnumerable<UnitDefinition<{_unitEnumName}>>, IEnumerable<IUnitDefinition<{_unitEnumName}>>> customizeUnits)
191+
{{
192+
return new {quantityInfoClassName}(nameof({_quantity.Name}), DefaultBaseUnit, customizeUnits(GetDefaultMappings()), new {_quantity.Name}(0, DefaultBaseUnit), DefaultBaseDimensions);
193+
}}
162194
195+
/// <summary>
196+
/// The <see cref=""BaseDimensions"" /> for <see cref=""{_quantity.Name}""/> is {_quantity.BaseDimensions}.
197+
/// </summary>
198+
public static BaseDimensions DefaultBaseDimensions {{ get; }} = {createDimensionsExpression};
199+
200+
/// <summary>
201+
/// The default base unit of {_quantity.Name} is {_baseUnit.SingularName}. All conversions, as defined in the <see cref=""GetDefaultMappings""/>, go via this value.
202+
/// </summary>
203+
public static {_unitEnumName} DefaultBaseUnit {{ get; }} = {_unitEnumName}.{_baseUnit.SingularName};
204+
205+
/// <summary>
206+
/// Retrieves the default mappings for <see cref=""{_unitEnumName}""/>.
207+
/// </summary>
208+
/// <returns>An <see cref=""IEnumerable{{T}}""/> of <see cref=""UnitDefinition{{{_unitEnumName}}}""/> representing the default unit mappings for {_quantity.Name}.</returns>
209+
public static IEnumerable<UnitDefinition<{_unitEnumName}>> GetDefaultMappings()
210+
{{");
211+
163212
foreach (Unit unit in _quantity.Units)
164213
{
165214
BaseUnits? baseUnits = unit.BaseUnits;
215+
string baseUnitsFormat;
166216
if (baseUnits == null)
167217
{
168-
Writer.WL($@"
169-
new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, ""{unit.PluralName}"", BaseUnits.Undefined, ""{_quantity.Name}""),");
218+
baseUnitsFormat = "BaseUnits.Undefined";
170219
}
171220
else
172221
{
173-
var baseUnitsCtorArgs = string.Join(", ",
222+
baseUnitsFormat = $"new BaseUnits({string.Join(", ",
174223
new[]
175224
{
176225
baseUnits.L != null ? $"length: LengthUnit.{baseUnits.L}" : null,
@@ -180,17 +229,26 @@ private void GenerateStaticConstructor()
180229
baseUnits.Θ != null ? $"temperature: TemperatureUnit.{baseUnits.Θ}" : null,
181230
baseUnits.N != null ? $"amount: AmountOfSubstanceUnit.{baseUnits.N}" : null,
182231
baseUnits.J != null ? $"luminousIntensity: LuminousIntensityUnit.{baseUnits.J}" : null
183-
}.Where(str => str != null));
184-
185-
Writer.WL($@"
186-
new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, ""{unit.PluralName}"", new BaseUnits({baseUnitsCtorArgs}), ""{_quantity.Name}""),");
232+
}.Where(str => str != null))})";
187233
}
234+
235+
Writer.WL($@"
236+
yield return new ({_unitEnumName}.{unit.SingularName}, ""{unit.SingularName}"", ""{unit.PluralName}"", {baseUnitsFormat});");
188237
}
189238

190239
Writer.WL($@"
191-
}},
192-
BaseUnit, Zero, BaseDimensions);
193-
240+
}}
241+
}}
242+
");
243+
}
244+
245+
private void GenerateStaticConstructor()
246+
{
247+
Writer.WL($@"
248+
static {_quantity.Name}()
249+
{{");
250+
Writer.WL($@"
251+
Info = {_quantity.Name}Info.CreateDefault();
194252
DefaultConversionFunctions = new UnitConverter();
195253
RegisterDefaultConversions(DefaultConversionFunctions);
196254
}}
@@ -244,27 +302,27 @@ private void GenerateStaticProperties()
244302
public static UnitConverter DefaultConversionFunctions {{ get; }}
245303
246304
/// <inheritdoc cref=""IQuantity.QuantityInfo""/>
247-
public static QuantityInfo<{_unitEnumName}> Info {{ get; }}
305+
public static QuantityInfo<{_quantity.Name}, {_unitEnumName}> Info {{ get; }}
248306
249307
/// <summary>
250308
/// The <see cref=""BaseDimensions"" /> of this quantity.
251309
/// </summary>
252-
public static BaseDimensions BaseDimensions {{ get; }}
310+
public static BaseDimensions BaseDimensions => Info.BaseDimensions;
253311
254312
/// <summary>
255313
/// The base unit of {_quantity.Name}, which is {_quantity.BaseUnit}. All conversions go via this value.
256314
/// </summary>
257-
public static {_unitEnumName} BaseUnit {{ get; }}
315+
public static {_unitEnumName} BaseUnit => Info.BaseUnitInfo.Value;
258316
259317
/// <summary>
260318
/// All units of measurement for the {_quantity.Name} quantity.
261319
/// </summary>
262-
public static {_unitEnumName}[] Units {{ get; }}
320+
public static IReadOnlyCollection<{_unitEnumName}> Units => Info.Units;
263321
264322
/// <summary>
265323
/// Gets an instance of this quantity with a value of 0 in the base unit {_quantity.BaseUnit}.
266324
/// </summary>
267-
public static {_quantity.Name} Zero {{ get; }}
325+
public static {_quantity.Name} Zero => Info.Zero;
268326
");
269327

270328
if (_quantity.GenerateArithmetic)
@@ -294,7 +352,7 @@ private void GenerateProperties()
294352
public {_unitEnumName} Unit => _unit.GetValueOrDefault(BaseUnit);
295353
296354
/// <inheritdoc />
297-
public QuantityInfo<{_unitEnumName}> QuantityInfo => Info;
355+
public QuantityInfo<{_quantity.Name}, {_unitEnumName}> QuantityInfo => Info;
298356
299357
/// <summary>
300358
/// The <see cref=""BaseDimensions"" /> of this quantity.
@@ -312,6 +370,9 @@ private void GenerateProperties()
312370
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
313371
QuantityInfo IQuantity.QuantityInfo => Info;
314372
373+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
374+
QuantityInfo<{_unitEnumName}> IQuantity<{_unitEnumName}>.QuantityInfo => Info;
375+
315376
#endregion
316377
317378
#endregion

CodeGen/Generators/UnitsNetGen/StaticQuantityGenerator.cs

Lines changed: 12 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using CodeGen.Helpers;
2-
using CodeGen.JsonTypes;
1+
using CodeGen.JsonTypes;
32

43
namespace CodeGen.Generators.UnitsNetGen
54
{
@@ -16,120 +15,31 @@ public string Generate()
1615
{
1716
Writer.WL(GeneratedFileHeader);
1817
Writer.WL(@"
19-
using System;
20-
using System.Globalization;
21-
using UnitsNet.Units;
22-
using System.Collections.Generic;
23-
using System.Diagnostics.CodeAnalysis;
24-
using System.Linq;
2518
2619
#nullable enable
2720
28-
namespace UnitsNet
21+
namespace UnitsNet;
22+
23+
/// <summary>
24+
/// Dynamically parse or construct quantities when types are only known at runtime.
25+
/// </summary>
26+
public partial class Quantity
2927
{
3028
/// <summary>
31-
/// Dynamically parse or construct quantities when types are only known at runtime.
29+
/// Serves as a repository for predefined quantity conversion mappings, facilitating the automatic generation and retrieval of unit conversions in the UnitsNet library.
3230
/// </summary>
33-
public partial class Quantity
31+
internal static class Provider
3432
{
3533
/// <summary>
36-
/// All QuantityInfo instances mapped by quantity name that are present in UnitsNet by default.
34+
/// All QuantityInfo instances that are present in UnitsNet by default.
3735
/// </summary>
38-
public static readonly IDictionary<string, QuantityInfo> ByName = new Dictionary<string, QuantityInfo>
36+
internal static IReadOnlyList<QuantityInfo> DefaultQuantities => new QuantityInfo[]
3937
{");
4038
foreach (var quantity in _quantities)
4139
Writer.WL($@"
42-
{{ ""{quantity.Name}"", {quantity.Name}.Info }},");
40+
{quantity.Name}.Info,");
4341
Writer.WL(@"
4442
};
45-
46-
/// <summary>
47-
/// Dynamically constructs a quantity of the given <see cref=""QuantityInfo""/> with the value in the quantity's base units.
48-
/// </summary>
49-
/// <param name=""quantityInfo"">The <see cref=""QuantityInfo""/> of the quantity to create.</param>
50-
/// <param name=""value"">The value to construct the quantity with.</param>
51-
/// <returns>The created quantity.</returns>
52-
public static IQuantity FromQuantityInfo(QuantityInfo quantityInfo, double value)
53-
{
54-
return quantityInfo.Name switch
55-
{");
56-
foreach (var quantity in _quantities)
57-
{
58-
var quantityName = quantity.Name;
59-
Writer.WL($@"
60-
""{quantityName}"" => {quantityName}.From(value, {quantityName}.BaseUnit),");
61-
}
62-
63-
Writer.WL(@"
64-
_ => throw new ArgumentException($""{quantityInfo.Name} is not a supported quantity."")
65-
};
66-
}
67-
68-
/// <summary>
69-
/// Try to dynamically construct a quantity.
70-
/// </summary>
71-
/// <param name=""value"">Numeric value.</param>
72-
/// <param name=""unit"">Unit enum value.</param>
73-
/// <param name=""quantity"">The resulting quantity if successful, otherwise <c>default</c>.</param>
74-
/// <returns><c>True</c> if successful with <paramref name=""quantity""/> assigned the value, otherwise <c>false</c>.</returns>
75-
public static bool TryFrom(double value, Enum? unit, [NotNullWhen(true)] out IQuantity? quantity)
76-
{
77-
quantity = unit switch
78-
{");
79-
foreach (var quantity in _quantities)
80-
{
81-
var quantityName = quantity.Name;
82-
var unitTypeName = $"{quantityName}Unit";
83-
var unitValue = unitTypeName.ToCamelCase();
84-
Writer.WL($@"
85-
{unitTypeName} {unitValue} => {quantityName}.From(value, {unitValue}),");
86-
}
87-
88-
Writer.WL(@"
89-
_ => null
90-
};
91-
92-
return quantity is not null;
93-
}
94-
95-
/// <summary>
96-
/// Try to dynamically parse a quantity string representation.
97-
/// </summary>
98-
/// <param name=""formatProvider"">The format provider to use for lookup. Defaults to <see cref=""CultureInfo.CurrentCulture"" /> if null.</param>
99-
/// <param name=""quantityType"">Type of quantity, such as <see cref=""Length""/>.</param>
100-
/// <param name=""quantityString"">Quantity string representation, such as ""1.5 kg"". Must be compatible with given quantity type.</param>
101-
/// <param name=""quantity"">The resulting quantity if successful, otherwise <c>default</c>.</param>
102-
/// <returns>The parsed quantity.</returns>
103-
public static bool TryParse(IFormatProvider? formatProvider, Type quantityType, [NotNullWhen(true)] string? quantityString, [NotNullWhen(true)] out IQuantity? quantity)
104-
{
105-
quantity = default(IQuantity);
106-
107-
if (!typeof(IQuantity).IsAssignableFrom(quantityType))
108-
return false;
109-
110-
var parser = UnitsNetSetup.Default.QuantityParser;
111-
112-
return quantityType switch
113-
{");
114-
foreach (var quantity in _quantities)
115-
{
116-
var quantityName = quantity.Name;
117-
Writer.WL($@"
118-
Type _ when quantityType == typeof({quantityName}) => parser.TryParse<{quantityName}, {quantityName}Unit>(quantityString, formatProvider, {quantityName}.From, out quantity),");
119-
}
120-
121-
Writer.WL(@"
122-
_ => false
123-
};
124-
}
125-
126-
internal static IEnumerable<Type> GetQuantityTypes()
127-
{");
128-
foreach (var quantity in _quantities)
129-
Writer.WL($@"
130-
yield return typeof({quantity.Name});");
131-
Writer.WL(@"
132-
}
13343
}
13444
}");
13545
return Writer.ToString();

0 commit comments

Comments
 (0)