Skip to content

Commit 5c49fb0

Browse files
lipchevangularsen
andauthored
Refactored the IQuantity interface (#1587)
- refactored the `IQuantity` interface: removed the `Equals` with tolerance and the `ToString` overloads - introduced `IAffineQuantity` (see `Temperature`), `ILogarithmicQuantity` (see `AmplitudeRatio`) and `ILinearQuantity` (see `Mass`) - introduced extension methods for `Equals` with tolerance as well as `Sum` and `Average` (as applicable) - replaced the implementation of the `GetHashCode` method with a call to a better optimized function in the `Comparison` class - removed the `Sum`/`Average` from `UnitMath` (replaced be the new extension methods using the _more concrete_ definitions) - marked the `GenericMathExtensions` class as `[Obsolete]` (replaced by the new extension methods) fixes #1461 --------- Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
1 parent 484966f commit 5c49fb0

File tree

291 files changed

+8602
-16419
lines changed

Some content is hidden

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

291 files changed

+8602
-16419
lines changed

CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs

Lines changed: 100 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,62 @@ namespace UnitsNet
6363
Writer.WL(@$"
6464
[DataContract]
6565
[DebuggerTypeProxy(typeof(QuantityDisplay))]
66-
public readonly partial struct {_quantity.Name} :
67-
{(_quantity.GenerateArithmetic ? "IArithmeticQuantity" : "IQuantity")}<{_quantity.Name}, {_unitEnumName}>,");
66+
public readonly partial struct {_quantity.Name} :");
67+
GenerateInterfaceExtensions();
68+
69+
Writer.WL($@"
70+
{{
71+
/// <summary>
72+
/// The numeric value this quantity was constructed with.
73+
/// </summary>
74+
[DataMember(Name = ""Value"", Order = 1)]
75+
private readonly double _value;
76+
77+
/// <summary>
78+
/// The unit this quantity was constructed with.
79+
/// </summary>
80+
[DataMember(Name = ""Unit"", Order = 2)]
81+
private readonly {_unitEnumName}? _unit;
82+
");
83+
GenerateQuantityInfo();
84+
GenerateStaticConstructor();
85+
GenerateInstanceConstructors();
86+
GenerateStaticProperties();
87+
GenerateProperties();
88+
GenerateConversionProperties();
89+
GenerateStaticMethods();
90+
GenerateStaticFactoryMethods();
91+
GenerateStaticParseMethods();
92+
GenerateArithmeticOperators();
93+
GenerateRelationalOperators();
94+
GenerateEqualityAndComparison();
95+
GenerateConversionMethods();
96+
GenerateToString();
97+
98+
Writer.WL($@"
99+
}}
100+
}}");
101+
return Writer.ToString();
102+
}
103+
104+
private void GenerateInterfaceExtensions()
105+
{
106+
// generate the base interface (either IVectorQuantity, IAffineQuantity or ILogarithmicQuantity)
107+
if (_quantity.Logarithmic)
108+
{
109+
Writer.WL(@$"
110+
ILogarithmicQuantity<{_quantity.Name}, {_unitEnumName}>,");
111+
}
112+
else if (!string.IsNullOrEmpty(_quantity.AffineOffsetType))
113+
{
114+
Writer.WL(@$"
115+
IAffineQuantity<{_quantity.Name}, {_unitEnumName}, {_quantity.AffineOffsetType}>,");
116+
}
117+
else // the default quantity type implements the IVectorQuantity interface
118+
{
119+
Writer.WL(@$"
120+
IArithmeticQuantity<{_quantity.Name}, {_unitEnumName}>,");
121+
}
68122

69123
if (_quantity.Relations.Any(r => r.Operator is "*" or "/"))
70124
{
@@ -94,7 +148,6 @@ namespace UnitsNet
94148
Writer.WL(@$"
95149
#endif");
96150
}
97-
98151
Writer.WL(@$"
99152
#if NET7_0_OR_GREATER
100153
IComparisonOperators<{_quantity.Name}, {_quantity.Name}, bool>,
@@ -104,42 +157,8 @@ namespace UnitsNet
104157
IComparable<{_quantity.Name}>,
105158
IEquatable<{_quantity.Name}>,
106159
IFormattable");
107-
108-
Writer.WL($@"
109-
{{
110-
/// <summary>
111-
/// The numeric value this quantity was constructed with.
112-
/// </summary>
113-
[DataMember(Name = ""Value"", Order = 1)]
114-
private readonly double _value;
115-
116-
/// <summary>
117-
/// The unit this quantity was constructed with.
118-
/// </summary>
119-
[DataMember(Name = ""Unit"", Order = 2)]
120-
private readonly {_unitEnumName}? _unit;
121-
");
122-
GenerateQuantityInfo();
123-
GenerateStaticConstructor();
124-
GenerateInstanceConstructors();
125-
GenerateStaticProperties();
126-
GenerateProperties();
127-
GenerateConversionProperties();
128-
GenerateStaticMethods();
129-
GenerateStaticFactoryMethods();
130-
GenerateStaticParseMethods();
131-
GenerateArithmeticOperators();
132-
GenerateRelationalOperators();
133-
GenerateEqualityAndComparison();
134-
GenerateConversionMethods();
135-
GenerateToString();
136-
137-
Writer.WL($@"
138-
}}
139-
}}");
140-
return Writer.ToString();
141160
}
142-
161+
143162
private void GenerateQuantityInfo()
144163
{
145164
var quantityInfoClassName = $"{_quantity.Name}Info";
@@ -324,15 +343,15 @@ private void GenerateStaticProperties()
324343
public static {_quantity.Name} Zero => Info.Zero;
325344
");
326345

327-
if (_quantity.GenerateArithmetic)
346+
if (_quantity.Logarithmic)
328347
{
329348
Writer.WL($@"
330-
/// <inheritdoc cref=""Zero""/>
331-
public static {_quantity.Name} AdditiveIdentity => Zero;
349+
/// <inheritdoc />
350+
public static double LogarithmicScalingFactor {{get;}} = {10 * _quantity.LogarithmicScalingFactor};
332351
");
333352
}
334353

335-
Writer.WL($@"
354+
Writer.WL(@"
336355
#endregion
337356
");
338357
}
@@ -372,6 +391,21 @@ private void GenerateProperties()
372391
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
373392
QuantityInfo<{_unitEnumName}> IQuantity<{_unitEnumName}>.QuantityInfo => Info;
374393
394+
#if NETSTANDARD2_0
395+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
396+
IQuantityInstanceInfo<{_quantity.Name}> IQuantityOfType<{_quantity.Name}>.QuantityInfo => Info;
397+
#endif
398+
");
399+
if (_quantity.Logarithmic)
400+
{
401+
Writer.WL($@"
402+
#if NETSTANDARD2_0
403+
double ILogarithmicQuantity<{_quantity.Name}>.LogarithmicScalingFactor => LogarithmicScalingFactor;
404+
#endif
405+
");
406+
}
407+
408+
Writer.WL(@"
375409
#endregion
376410
377411
#endregion
@@ -658,7 +692,12 @@ public static bool TryParseUnit([NotNullWhen(true)]string? str, IFormatProvider?
658692

659693
private void GenerateArithmeticOperators()
660694
{
661-
if (!_quantity.GenerateArithmetic) return;
695+
if (_quantity.IsAffine)
696+
{
697+
// the generation of arithmetic operators with affine quantities, such as Temperature + TemperatureDelta, is currently not supported
698+
// TODO see about handling this case using the UnitRelations
699+
return;
700+
}
662701

663702
// Logarithmic units required different arithmetic
664703
if (_quantity.Logarithmic)
@@ -919,6 +958,15 @@ public bool Equals({_quantity.Name} other)
919958
920959
#pragma warning restore CS0809
921960
961+
/// <summary>
962+
/// Returns the hash code for this instance.
963+
/// </summary>
964+
/// <returns>A hash code for the current {_quantity.Name}.</returns>
965+
public override int GetHashCode()
966+
{{
967+
return Comparison.GetHashCode(Unit, Value);
968+
}}
969+
922970
/// <summary>Compares the current <see cref=""{_quantity.Name}""/> 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.</summary>
923971
/// <param name=""obj"">An object to compare with this instance.</param>
924972
/// <exception cref=""T:System.ArgumentException"">
@@ -955,88 +1003,6 @@ public int CompareTo({_quantity.Name} other)
9551003
return _value.CompareTo(other.ToUnit(this.Unit).Value);
9561004
}}
9571005
958-
/// <summary>
959-
/// <para>
960-
/// Compare equality to another {_quantity.Name} within the given absolute or relative tolerance.
961-
/// </para>
962-
/// <para>
963-
/// Relative tolerance is defined as the maximum allowable absolute difference between this quantity's value and
964-
/// <paramref name=""other""/> as a percentage of this quantity's value. <paramref name=""other""/> will be converted into
965-
/// this quantity's unit for comparison. A relative tolerance of 0.01 means the absolute difference must be within +/- 1% of
966-
/// this quantity's value to be considered equal.
967-
/// <example>
968-
/// In this example, the two quantities will be equal if the value of b is within +/- 1% of a (0.02m or 2cm).
969-
/// <code>
970-
/// var a = Length.FromMeters(2.0);
971-
/// var b = Length.FromInches(50.0);
972-
/// a.Equals(b, 0.01, ComparisonType.Relative);
973-
/// </code>
974-
/// </example>
975-
/// </para>
976-
/// <para>
977-
/// Absolute tolerance is defined as the maximum allowable absolute difference between this quantity's value and
978-
/// <paramref name=""other""/> as a fixed number in this quantity's unit. <paramref name=""other""/> will be converted into
979-
/// this quantity's unit for comparison.
980-
/// <example>
981-
/// In this example, the two quantities will be equal if the value of b is within 0.01 of a (0.01m or 1cm).
982-
/// <code>
983-
/// var a = Length.FromMeters(2.0);
984-
/// var b = Length.FromInches(50.0);
985-
/// a.Equals(b, 0.01, ComparisonType.Absolute);
986-
/// </code>
987-
/// </example>
988-
/// </para>
989-
/// <para>
990-
/// Note that it is advised against specifying zero difference, due to the nature
991-
/// of floating-point operations and using double internally.
992-
/// </para>
993-
/// </summary>
994-
/// <param name=""other"">The other quantity to compare to.</param>
995-
/// <param name=""tolerance"">The absolute or relative tolerance value. Must be greater than or equal to 0.</param>
996-
/// <param name=""comparisonType"">The comparison type: either relative or absolute.</param>
997-
/// <returns>True if the absolute difference between the two values is not greater than the specified relative or absolute tolerance.</returns>
998-
[Obsolete(""Use Equals({_quantity.Name} other, {_quantity.Name} tolerance) instead, to check equality across units and to specify the max tolerance for rounding errors due to floating-point arithmetic when converting between units."")]
999-
public bool Equals({_quantity.Name} other, double tolerance, ComparisonType comparisonType)
1000-
{{
1001-
if (tolerance < 0)
1002-
throw new ArgumentOutOfRangeException(nameof(tolerance), ""Tolerance must be greater than or equal to 0."");
1003-
1004-
return UnitsNet.Comparison.Equals(
1005-
referenceValue: this.Value,
1006-
otherValue: other.As(this.Unit),
1007-
tolerance: tolerance,
1008-
comparisonType: comparisonType);
1009-
}}
1010-
1011-
/// <inheritdoc />
1012-
public bool Equals(IQuantity? other, IQuantity tolerance)
1013-
{{
1014-
return other is {_quantity.Name} otherTyped
1015-
&& (tolerance is {_quantity.Name} toleranceTyped
1016-
? true
1017-
: throw new ArgumentException($""Tolerance quantity ({{tolerance.QuantityInfo.Name}}) did not match the other quantities of type '{_quantity.Name}'."", nameof(tolerance)))
1018-
&& Equals(otherTyped, toleranceTyped);
1019-
}}
1020-
1021-
/// <inheritdoc />
1022-
public bool Equals({_quantity.Name} other, {_quantity.Name} tolerance)
1023-
{{
1024-
return UnitsNet.Comparison.Equals(
1025-
referenceValue: this.Value,
1026-
otherValue: other.As(this.Unit),
1027-
tolerance: tolerance.As(this.Unit),
1028-
comparisonType: ComparisonType.Absolute);
1029-
}}
1030-
1031-
/// <summary>
1032-
/// Returns the hash code for this instance.
1033-
/// </summary>
1034-
/// <returns>A hash code for the current {_quantity.Name}.</returns>
1035-
public override int GetHashCode()
1036-
{{
1037-
return new {{ Info.Name, Value, Unit }}.GetHashCode();
1038-
}}
1039-
10401006
#endregion
10411007
");
10421008
}
@@ -1061,6 +1027,15 @@ public double As({_unitEnumName} unit)
10611027

10621028
Writer.WL( $@"
10631029
1030+
/// <inheritdoc cref=""IQuantity.As(UnitKey)""/>
1031+
public double As(UnitKey unitKey)
1032+
{{
1033+
return As(unitKey.ToUnit<{_unitEnumName}>());
1034+
}}
1035+
");
1036+
1037+
Writer.WL( $@"
1038+
10641039
/// <inheritdoc cref=""IQuantity.As(UnitSystem)""/>
10651040
public double As(UnitSystem unitSystem)
10661041
{{
@@ -1222,27 +1197,6 @@ public override string ToString()
12221197
return ToString(null, null);
12231198
}}
12241199
1225-
/// <summary>
1226-
/// Gets the default string representation of value and unit using the given format provider.
1227-
/// </summary>
1228-
/// <returns>String representation.</returns>
1229-
/// <param name=""provider"">Format to use for localization and number formatting. Defaults to <see cref=""CultureInfo.CurrentCulture"" /> if null.</param>
1230-
public string ToString(IFormatProvider? provider)
1231-
{{
1232-
return ToString(null, provider);
1233-
}}
1234-
1235-
/// <inheritdoc cref=""QuantityFormatter.Format{{TQuantity}}(TQuantity, string?, IFormatProvider?)""/>
1236-
/// <summary>
1237-
/// Gets the string representation of this instance in the specified format string using <see cref=""CultureInfo.CurrentCulture"" />.
1238-
/// </summary>
1239-
/// <param name=""format"">The format string.</param>
1240-
/// <returns>The string representation.</returns>
1241-
public string ToString(string? format)
1242-
{{
1243-
return ToString(format, null);
1244-
}}
1245-
12461200
/// <inheritdoc cref=""QuantityFormatter.Format{{TQuantity}}(TQuantity, string?, IFormatProvider?)""/>
12471201
/// <summary>
12481202
/// Gets the string representation of this instance in the specified format string using the specified format provider, or <see cref=""CultureInfo.CurrentCulture"" /> if null.

0 commit comments

Comments
 (0)