Skip to content

Commit d59bfe6

Browse files
committed
fixup! Move unit abbreviation strings into culture-specific resource files (#1210)
Fixes 37ada11 when merging #1210 The PR incorrectly handled some merge conflicts and wound up deleting code. Regenerate code.
1 parent 37ada11 commit d59bfe6

12 files changed

+217
-79
lines changed

UnitsNet.Tests/CustomQuantities/HowMuch.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ public HowMuch(double value, HowMuchUnit unit)
3232
typeof(HowMuchUnit),
3333
new UnitInfo[]
3434
{
35-
new UnitInfo<HowMuchUnit>(HowMuchUnit.Some, "Some", BaseUnits.Undefined),
36-
new UnitInfo<HowMuchUnit>(HowMuchUnit.ATon, "Tons", BaseUnits.Undefined),
37-
new UnitInfo<HowMuchUnit>(HowMuchUnit.AShitTon, "ShitTons", BaseUnits.Undefined),
35+
new UnitInfo<HowMuchUnit>(HowMuchUnit.Some, "Some", BaseUnits.Undefined, nameof(HowMuch)),
36+
new UnitInfo<HowMuchUnit>(HowMuchUnit.ATon, "Tons", BaseUnits.Undefined, nameof(HowMuch)),
37+
new UnitInfo<HowMuchUnit>(HowMuchUnit.AShitTon, "ShitTons", BaseUnits.Undefined, nameof(HowMuch)),
3838
},
3939
HowMuchUnit.Some,
4040
Zero,

UnitsNet.Tests/QuantityInfoTest.cs

+11-11
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ public void Constructor_AssignsProperties()
1919
{
2020
var expectedZero = Length.FromCentimeters(10);
2121
UnitInfo[] expectedUnitInfos = {
22-
new(LengthUnit.Centimeter, "Centimeters", new BaseUnits(LengthUnit.Centimeter)),
23-
new(LengthUnit.Kilometer, "Kilometers", new BaseUnits(LengthUnit.Kilometer))
22+
new(LengthUnit.Centimeter, "Centimeters", new BaseUnits(LengthUnit.Centimeter), nameof(Length)),
23+
new(LengthUnit.Kilometer, "Kilometers", new BaseUnits(LengthUnit.Kilometer), nameof(Length))
2424
};
2525
var expectedBaseUnit = LengthUnit.Centimeter;
2626
var expectedBaseDimensions = Length.BaseDimensions;
@@ -39,9 +39,9 @@ public void Constructor_AssignsPropertiesForCustomQuantity()
3939
{
4040
var expectedZero = new HowMuch(10, HowMuchUnit.Some);
4141
UnitInfo[] expectedUnitInfos = {
42-
new(HowMuchUnit.Some, "Some", BaseUnits.Undefined),
43-
new(HowMuchUnit.ATon, "Tons", BaseUnits.Undefined),
44-
new(HowMuchUnit.AShitTon, "ShitTons", BaseUnits.Undefined)
42+
new(HowMuchUnit.Some, "Some", BaseUnits.Undefined, nameof(HowMuch)),
43+
new(HowMuchUnit.ATon, "Tons", BaseUnits.Undefined, nameof(HowMuch)),
44+
new(HowMuchUnit.AShitTon, "ShitTons", BaseUnits.Undefined, nameof(HowMuch))
4545
};
4646
var expectedBaseUnit = HowMuchUnit.Some;
4747
var expectedBaseDimensions = BaseDimensions.Dimensionless;
@@ -60,8 +60,8 @@ public void GenericsConstructor_AssignsProperties()
6060
{
6161
var expectedZero = Length.FromCentimeters(10);
6262
UnitInfo<LengthUnit>[] expectedUnitInfos = {
63-
new(LengthUnit.Centimeter, "Centimeters", new BaseUnits(LengthUnit.Centimeter)),
64-
new(LengthUnit.Kilometer,"Kilometers", new BaseUnits(LengthUnit.Kilometer))
63+
new(LengthUnit.Centimeter, "Centimeters", new BaseUnits(LengthUnit.Centimeter), nameof(Length)),
64+
new(LengthUnit.Kilometer,"Kilometers", new BaseUnits(LengthUnit.Kilometer), nameof(Length))
6565
};
6666
var expectedBaseUnit = LengthUnit.Centimeter;
6767
var expectedBaseDimensions = Length.BaseDimensions;
@@ -204,8 +204,8 @@ public void GetUnitInfoFor_GivenBaseUnitsWithMultipleMatches_ThrowsInvalidOperat
204204

205205
var quantityInfo = new QuantityInfo<LengthUnit>(Length.Info.Name,
206206
new UnitInfo<LengthUnit>[] {
207-
new(LengthUnit.Meter, "Meters", baseUnits),
208-
new(LengthUnit.Foot, "Feet", baseUnits)
207+
new(LengthUnit.Meter, "Meters", baseUnits, nameof(Length)),
208+
new(LengthUnit.Foot, "Feet", baseUnits, nameof(Length))
209209
},
210210
LengthUnit.Meter, Length.Zero, Length.BaseDimensions);
211211

@@ -242,8 +242,8 @@ public void GetUnitInfosFor_GivenBaseUnitsWithMultipleMatches_ReturnsMultipleMat
242242

243243
var quantityInfo = new QuantityInfo<LengthUnit>(Length.Info.Name,
244244
new UnitInfo<LengthUnit>[] {
245-
new(LengthUnit.Meter, "Meters", baseUnits),
246-
new(LengthUnit.Foot, "Feet", baseUnits) },
245+
new(LengthUnit.Meter, "Meters", baseUnits, nameof(Length)),
246+
new(LengthUnit.Foot, "Feet", baseUnits, nameof(Length)) },
247247
LengthUnit.Meter, Length.Zero, Length.BaseDimensions);
248248

249249
var result = quantityInfo.GetUnitInfosFor(baseUnits);

UnitsNet.Tests/QuantityTypeConverterTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public void ConvertFrom_GivenEmptyString_ThrowsNotSupportedException()
132132
}
133133

134134
[Fact]
135-
public void ConvertFrom_GivenWrongQuantity_ThrowsArgumentException()
135+
public void ConvertFrom_GivenWrongQuantity_ThrowsUnitNotFoundException()
136136
{
137137
var converter = new QuantityTypeConverter<Length>();
138138
ITypeDescriptorContext context = new TypeDescriptorContext("SomeMemberName", new Attribute[] { });

UnitsNet.Tests/UnitInfoTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ public class UnitInfoTests
1111
[Fact]
1212
public void ConstructorTest()
1313
{
14-
var unitInfo = new UnitInfo(LengthUnit.Meter, "Meters", new BaseUnits(LengthUnit.Meter));
14+
var unitInfo = new UnitInfo(LengthUnit.Meter, "Meters", new BaseUnits(LengthUnit.Meter), nameof(Length));
1515
Assert.Equal(LengthUnit.Meter, unitInfo.Value);
1616
Assert.Equal(LengthUnit.Meter.ToString(), unitInfo.Name);
1717
}
1818

1919
[Fact]
2020
public void GenericConstructorTest()
2121
{
22-
var unitInfo = new UnitInfo<LengthUnit>(LengthUnit.Meter, "Meters", new BaseUnits(LengthUnit.Meter));
22+
var unitInfo = new UnitInfo<LengthUnit>(LengthUnit.Meter, "Meters", new BaseUnits(LengthUnit.Meter), nameof(Length));
2323
Assert.Equal(LengthUnit.Meter, unitInfo.Value);
2424
Assert.Equal(LengthUnit.Meter.ToString(), unitInfo.Name);
2525
}

UnitsNet/CustomCode/Quantity.cs

+144-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Diagnostics.CodeAnalysis;
44
using System.Globalization;
5+
using System.Linq;
56
using UnitsNet.Units;
67

78
namespace UnitsNet
@@ -40,7 +41,7 @@ public static bool TryGetUnitInfo(Enum unitEnum, [NotNullWhen(true)] out UnitInf
4041
Default.TryGetUnitInfo(unitEnum, out unitInfo);
4142

4243
/// <summary>
43-
///
44+
///
4445
/// </summary>
4546
/// <param name="unit"></param>
4647
/// <param name="unitInfo"></param>
@@ -55,13 +56,153 @@ public static bool TryGetUnitInfo(Enum unitEnum, [NotNullWhen(true)] out UnitInf
5556
/// <exception cref="UnitNotFoundException">Unit value is not a known unit enum type.</exception>
5657
public static IQuantity From(QuantityValue value, Enum unit)
5758
{
58-
return Default.From(value, unit);
59+
return TryFrom(value, unit, out IQuantity? quantity)
60+
? quantity
61+
: throw new UnitNotFoundException($"Unit value {unit} of type {unit.GetType()} is not a known unit enum type. Expected types like UnitsNet.Units.LengthUnit. Did you pass in a custom enum type defined outside the UnitsNet library?");
62+
}
63+
64+
/// <summary>
65+
/// Dynamically construct a quantity from a value, the quantity name and the unit name.
66+
/// </summary>
67+
/// <param name="value">Numeric value.</param>
68+
/// <param name="quantityName">The invariant quantity name, such as "Length". Does not support localization.</param>
69+
/// <param name="unitName">The invariant unit enum name, such as "Meter". Does not support localization.</param>
70+
/// <returns>An <see cref="IQuantity"/> object.</returns>
71+
/// <exception cref="ArgumentException">Unit value is not a known unit enum type.</exception>
72+
public static IQuantity From(QuantityValue value, string quantityName, string unitName)
73+
{
74+
// Get enum value for this unit, f.ex. LengthUnit.Meter for unit name "Meter".
75+
return UnitConverter.TryParseUnit(quantityName, unitName, out Enum? unitValue) &&
76+
TryFrom(value, unitValue, out IQuantity? quantity)
77+
? quantity
78+
: throw new UnitNotFoundException($"Unit [{unitName}] not found for quantity [{quantityName}].");
79+
}
80+
81+
/// <summary>
82+
/// Dynamically construct a quantity from a numeric value and a unit abbreviation using <see cref="CultureInfo.CurrentCulture"/>.
83+
/// </summary>
84+
/// <remarks>
85+
/// This method is currently not optimized for performance and will enumerate all units and their unit abbreviations each time.<br/>
86+
/// Unit abbreviation matching is case-insensitive.<br/>
87+
/// <br/>
88+
/// This will fail if more than one unit across all quantities share the same unit abbreviation.<br/>
89+
/// Prefer <see cref="From(UnitsNet.QuantityValue,System.Enum)"/> or <see cref="From(UnitsNet.QuantityValue,string,string)"/> instead.
90+
/// </remarks>
91+
/// <param name="value">Numeric value.</param>
92+
/// <param name="unitAbbreviation">Unit abbreviation, such as "kg" for <see cref="MassUnit.Kilogram"/>.</param>
93+
/// <returns>An <see cref="IQuantity"/> object.</returns>
94+
/// <exception cref="UnitNotFoundException">Unit abbreviation is not known.</exception>
95+
/// <exception cref="AmbiguousUnitParseException">Multiple units found matching the given unit abbreviation.</exception>
96+
public static IQuantity FromUnitAbbreviation(QuantityValue value, string unitAbbreviation) => FromUnitAbbreviation(null, value, unitAbbreviation);
97+
98+
/// <summary>
99+
/// Dynamically construct a quantity from a numeric value and a unit abbreviation.
100+
/// </summary>
101+
/// <remarks>
102+
/// This method is currently not optimized for performance and will enumerate all units and their unit abbreviations each time.<br/>
103+
/// Unit abbreviation matching is case-insensitive.<br/>
104+
/// <br/>
105+
/// This will fail if more than one unit across all quantities share the same unit abbreviation.<br/>
106+
/// Prefer <see cref="From(UnitsNet.QuantityValue,System.Enum)"/> or <see cref="From(UnitsNet.QuantityValue,string,string)"/> instead.
107+
/// </remarks>
108+
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
109+
/// <param name="value">Numeric value.</param>
110+
/// <param name="unitAbbreviation">Unit abbreviation, such as "kg" for <see cref="MassUnit.Kilogram"/>.</param>
111+
/// <returns>An <see cref="IQuantity"/> object.</returns>
112+
/// <exception cref="UnitNotFoundException">Unit abbreviation is not known.</exception>
113+
/// <exception cref="AmbiguousUnitParseException">Multiple units found matching the given unit abbreviation.</exception>
114+
public static IQuantity FromUnitAbbreviation(IFormatProvider? formatProvider, QuantityValue value, string unitAbbreviation)
115+
{
116+
// TODO Optimize this with UnitValueAbbreviationLookup via UnitAbbreviationsCache.TryGetUnitValueAbbreviationLookup.
117+
List<Enum> units = GetUnitsForAbbreviation(formatProvider, unitAbbreviation);
118+
if (units.Count > 1)
119+
{
120+
throw new AmbiguousUnitParseException($"Multiple units found matching the given unit abbreviation: {unitAbbreviation}");
121+
}
122+
123+
if (units.Count == 0)
124+
{
125+
throw new UnitNotFoundException($"Unit abbreviation {unitAbbreviation} is not known. Did you pass in a custom unit abbreviation defined outside the UnitsNet library? This is currently not supported.");
126+
}
127+
128+
Enum unit = units.Single();
129+
return From(value, unit);
59130
}
60131

61132
/// <inheritdoc cref="TryFrom(QuantityValue,System.Enum,out UnitsNet.IQuantity)"/>
62133
public static bool TryFrom(double value, Enum unit, [NotNullWhen(true)] out IQuantity? quantity)
63134
{
64-
return Default.TryFrom(value, unit, out quantity);
135+
quantity = default;
136+
137+
// Implicit cast to QuantityValue would prevent TryFrom from being called,
138+
// so we need to explicitly check this here for double arguments.
139+
return !double.IsNaN(value) &&
140+
!double.IsInfinity(value) &&
141+
TryFrom((QuantityValue)value, unit, out quantity);
142+
}
143+
144+
/// <summary>
145+
/// Try to dynamically construct a quantity from a value, the quantity name and the unit name.
146+
/// </summary>
147+
/// <param name="value">Numeric value.</param>
148+
/// <param name="unitName">The invariant unit enum name, such as "Meter". Does not support localization.</param>
149+
/// <param name="quantityName">The invariant quantity name, such as "Length". Does not support localization.</param>
150+
/// <param name="quantity">The constructed quantity, if successful, otherwise null.</param>
151+
/// <returns><c>True</c> if successful with <paramref name="quantity"/> assigned the value, otherwise <c>false</c>.</returns>
152+
public static bool TryFrom(double value, string quantityName, string unitName, [NotNullWhen(true)] out IQuantity? quantity)
153+
{
154+
quantity = default;
155+
156+
return UnitConverter.TryParseUnit(quantityName, unitName, out Enum? unitValue) &&
157+
TryFrom(value, unitValue, out quantity);
158+
}
159+
160+
/// <summary>
161+
/// Dynamically construct a quantity from a numeric value and a unit abbreviation using <see cref="CultureInfo.CurrentCulture"/>.
162+
/// </summary>
163+
/// <remarks>
164+
/// This method is currently not optimized for performance and will enumerate all units and their unit abbreviations each time.<br/>
165+
/// Unit abbreviation matching is case-insensitive.<br/>
166+
/// <br/>
167+
/// This will fail if more than one unit across all quantities share the same unit abbreviation.<br/>
168+
/// Prefer <see cref="From(UnitsNet.QuantityValue,System.Enum)"/> or <see cref="From(UnitsNet.QuantityValue,string,string)"/> instead.
169+
/// </remarks>
170+
/// <param name="value">Numeric value.</param>
171+
/// <param name="unitAbbreviation">Unit abbreviation, such as "kg" for <see cref="MassUnit.Kilogram"/>.</param>
172+
/// <param name="quantity">The quantity if successful, otherwise null.</param>
173+
/// <returns>True if successful.</returns>
174+
/// <exception cref="ArgumentException">Unit value is not a known unit enum type.</exception>
175+
public static bool TryFromUnitAbbreviation(QuantityValue value, string unitAbbreviation, [NotNullWhen(true)] out IQuantity? quantity) =>
176+
TryFromUnitAbbreviation(null, value, unitAbbreviation, out quantity);
177+
178+
/// <summary>
179+
/// Dynamically construct a quantity from a numeric value and a unit abbreviation.
180+
/// </summary>
181+
/// <remarks>
182+
/// This method is currently not optimized for performance and will enumerate all units and their unit abbreviations each time.<br/>
183+
/// Unit abbreviation matching is case-insensitive.<br/>
184+
/// <br/>
185+
/// This will fail if more than one unit across all quantities share the same unit abbreviation.<br/>
186+
/// Prefer <see cref="From(UnitsNet.QuantityValue,System.Enum)"/> or <see cref="From(UnitsNet.QuantityValue,string,string)"/> instead.
187+
/// </remarks>
188+
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
189+
/// <param name="value">Numeric value.</param>
190+
/// <param name="unitAbbreviation">Unit abbreviation, such as "kg" for <see cref="MassUnit.Kilogram"/>.</param>
191+
/// <param name="quantity">The quantity if successful, otherwise null.</param>
192+
/// <returns>True if successful.</returns>
193+
/// <exception cref="ArgumentException">Unit value is not a known unit enum type.</exception>
194+
public static bool TryFromUnitAbbreviation(IFormatProvider? formatProvider, QuantityValue value, string unitAbbreviation, [NotNullWhen(true)] out IQuantity? quantity)
195+
{
196+
// TODO Optimize this with UnitValueAbbreviationLookup via UnitAbbreviationsCache.TryGetUnitValueAbbreviationLookup.
197+
List<Enum> units = GetUnitsForAbbreviation(formatProvider, unitAbbreviation);
198+
if (units.Count == 1)
199+
{
200+
Enum? unit = units.SingleOrDefault();
201+
return TryFrom(value, unit, out quantity);
202+
}
203+
204+
quantity = default;
205+
return false;
65206
}
66207

67208
/// <inheritdoc cref="Parse(IFormatProvider, System.Type,string)"/>

0 commit comments

Comments
 (0)