Skip to content

Commit 5e88d20

Browse files
committed
Adding some comments in the QuantityGenerator regarding the UnitInfo constructor logic and improving the xml comments regarding the equality contract
1 parent 226e7b2 commit 5e88d20

File tree

129 files changed

+4287
-645
lines changed

Some content is hidden

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

129 files changed

+4287
-645
lines changed

CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs

Lines changed: 57 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -252,15 +252,19 @@ public sealed class {quantityInfoClassName}: QuantityInfo<{_quantity.Name}, {_un
252252
}.Where(str => str != null))})";
253253
}
254254

255+
// the UnitInfo constructor has 3 overloads:
256+
// - one for the base unit without conversion expressions
257+
// - one for units with only FromBaseToUnit conversion expression (with the FromUnitToBase expression assumed to be the inverse)
258+
// - one for units with both FromBaseToUnit and FromUnitToBase conversion expressions (required when the conversion is not a simple inverse, e.g. affine conversions)
255259
if (unit.SingularName == _quantity.BaseUnit)
256260
{
257261
Writer.WL($@"
258262
yield return new ({_unitEnumName}.{unit.SingularName}, ""{unit.SingularName}"", ""{unit.PluralName}"", {baseUnitsFormat});");
259263
}
260264
else
261265
{
262-
// note: omitting the extra parameter (where possible) saves us 36 KB
263266
CompositeExpression expressionFromBaseToUnit = ExpressionEvaluator.Evaluate(unit.FromBaseToUnitFunc, "{x}");
267+
// Check if FromUnitToBase is simply the inverse of FromBaseToUnit
264268
if (expressionFromBaseToUnit.Terms.Count == 1 && expressionFromBaseToUnit.Degree == Fraction.One)
265269
{
266270
Writer.WL($@"
@@ -732,9 +736,7 @@ private void GenerateArithmeticOperators()
732736

733737
private void GenerateLogarithmicArithmeticOperators()
734738
{
735-
var scalingFactor = _quantity.LogarithmicScalingFactor;
736739
// Most logarithmic operators need a simple scaling factor of 10. However, certain units such as voltage ratio need to use 20 instead.
737-
var x = (10 * scalingFactor).ToString();
738740
Writer.WL($@"
739741
#region Logarithmic Arithmetic Operators
740742
@@ -750,10 +752,7 @@ private void GenerateLogarithmicArithmeticOperators()
750752
/// </remarks>
751753
public static {_quantity.Name} operator +({_quantity.Name} left, {_quantity.Name} right)
752754
{{
753-
// Logarithmic addition
754-
// Formula: {x} * log10(10^(x/{x}) + 10^(y/{x}))
755-
var leftUnit = left.Unit;
756-
return new {_quantity.Name}(QuantityValueExtensions.AddWithLogScaling(left.Value, right.As(leftUnit), LogarithmicScalingFactor), leftUnit);
755+
return new {_quantity.Name}(QuantityValueExtensions.AddWithLogScaling(left.Value, right.As(left.Unit), LogarithmicScalingFactor), left.Unit);
757756
}}
758757
759758
/// <summary>Get <see cref=""{_quantity.Name}""/> from logarithmic subtraction of two <see cref=""{_quantity.Name}""/>.</summary>
@@ -762,37 +761,30 @@ private void GenerateLogarithmicArithmeticOperators()
762761
/// </remarks>
763762
public static {_quantity.Name} operator -({_quantity.Name} left, {_quantity.Name} right)
764763
{{
765-
// Logarithmic subtraction
766-
// Formula: {x} * log10(10^(x/{x}) - 10^(y/{x}))
767-
var leftUnit = left.Unit;
768-
return new {_quantity.Name}(QuantityValueExtensions.SubtractWithLogScaling(left.Value, right.As(leftUnit), LogarithmicScalingFactor), leftUnit);
764+
return new {_quantity.Name}(QuantityValueExtensions.SubtractWithLogScaling(left.Value, right.As(left.Unit), LogarithmicScalingFactor), left.Unit);
769765
}}
770766
771767
/// <summary>Get <see cref=""{_quantity.Name}""/> from logarithmic multiplication of value and <see cref=""{_quantity.Name}""/>.</summary>
772768
public static {_quantity.Name} operator *(QuantityValue left, {_quantity.Name} right)
773769
{{
774-
// Logarithmic multiplication = addition
775770
return new {_quantity.Name}(left + right.Value, right.Unit);
776771
}}
777772
778773
/// <summary>Get <see cref=""{_quantity.Name}""/> from logarithmic multiplication of value and <see cref=""{_quantity.Name}""/>.</summary>
779774
public static {_quantity.Name} operator *({_quantity.Name} left, QuantityValue right)
780775
{{
781-
// Logarithmic multiplication = addition
782776
return new {_quantity.Name}(left.Value + right, left.Unit);
783777
}}
784778
785779
/// <summary>Get <see cref=""{_quantity.Name}""/> from logarithmic division of <see cref=""{_quantity.Name}""/> by value.</summary>
786780
public static {_quantity.Name} operator /({_quantity.Name} left, QuantityValue right)
787781
{{
788-
// Logarithmic division = subtraction
789782
return new {_quantity.Name}(left.Value - right, left.Unit);
790783
}}
791784
792785
/// <summary>Get ratio value from logarithmic division of <see cref=""{_quantity.Name}""/> by <see cref=""{_quantity.Name}""/>.</summary>
793786
public static QuantityValue operator /({_quantity.Name} left, {_quantity.Name} right)
794787
{{
795-
// Logarithmic division = subtraction
796788
return left.Value - right.As(left.Unit);
797789
}}
798790
@@ -801,11 +793,16 @@ private void GenerateLogarithmicArithmeticOperators()
801793
}
802794

803795
/// <summary>
804-
/// Generates operators that express relations between quantities as applied by <see cref="QuantityRelationsParser" />.
796+
/// Generates relational operators for quantities based on their defined relations.
805797
/// </summary>
806-
private void GenerateRelationalOperators()
798+
/// <param name="inverseWithFixedUnit">
799+
/// Specifies whether inverse relational operators should be generated as implicit conversions or simply using the unit specified in the UnitRelations.
800+
/// If <c>true</c>, the method generates inverse operators that convert to a fixed unit, as specified in the UnitRelations.
801+
/// If <c>false</c>, the method generates inverse operators as implicit conversions that utilize the UnitConverter for conversion.
802+
/// </param>
803+
private void GenerateRelationalOperators(bool inverseWithFixedUnit = false)
807804
{
808-
if (!_quantity.Relations.Any()) return;
805+
if (_quantity.Relations.Length == 0) return;
809806

810807
Writer.WL($@"
811808
#region Relational Operators
@@ -815,96 +812,30 @@ private void GenerateRelationalOperators()
815812
{
816813
if (relation.Operator == "inverse")
817814
{
818-
Writer.WL($@"
815+
if (inverseWithFixedUnit)
816+
{
817+
// this was the original behavior where the inverse always used the fixed unit from the relation
818+
Writer.WL($@"
819819
/// <summary>Calculates the inverse of this quantity.</summary>
820820
/// <returns>The corresponding inverse quantity, <see cref=""{relation.RightQuantity.Name}""/>.</returns>
821821
public {relation.RightQuantity.Name} Inverse()
822822
{{
823-
return UnitConverter.Default.ConvertTo(Value, Unit, {relation.RightQuantity.Name}.Info);
823+
return {relation.RightQuantity.Name}.From{relation.RightUnit.PluralName}(QuantityValue.Inverse({relation.LeftUnit.PluralName}));
824824
}}
825825
");
826-
}
827-
else
828-
{
829-
var leftParameterType = relation.LeftQuantity.Name;
830-
var leftParameterName = leftParameterType.ToCamelCase();
831-
var leftConversionProperty = relation.LeftUnit.PluralName;
832-
var rightParameterType = relation.RightQuantity.Name;
833-
var rightParameterName = relation.RightQuantity.Name.ToCamelCase();
834-
var rightConversionProperty = relation.RightUnit.PluralName;
835-
836-
if (leftParameterName == rightParameterName)
837-
{
838-
leftParameterName = "left";
839-
rightParameterName = "right";
840-
}
841-
842-
var leftPart = $"{leftParameterName}.{leftConversionProperty}";
843-
var rightPart = $"{rightParameterName}.{rightConversionProperty}";
844-
845-
if (leftParameterName is "double")
846-
{
847-
leftParameterType = "QuantityValue";
848-
leftParameterName = leftPart = "value";
849-
}
850-
851-
if (rightParameterName is "double")
852-
{
853-
rightParameterType = "QuantityValue";
854-
rightParameterName = rightPart = "value";
855-
}
856-
857-
var expression = $"{leftPart} {relation.Operator} {rightPart}";
858-
859-
var resultType = relation.ResultQuantity.Name;
860-
if (resultType is "double")
861-
{
862-
resultType = "QuantityValue";
863826
}
864827
else
865828
{
866-
expression = $"{resultType}.From{relation.ResultUnit.PluralName}({expression})";
867-
}
868-
869-
Writer.WL($@"
870-
/// <summary>Get <see cref=""{resultType}""/> from <see cref=""{leftParameterType}""/> {relation.Operator} <see cref=""{rightParameterType}""/>.</summary>
871-
public static {resultType} operator {relation.Operator}({leftParameterType} {leftParameterName}, {rightParameterType} {rightParameterName})
872-
{{
873-
return {expression};
874-
}}
875-
");
876-
}
877-
}
878-
879-
Writer.WL($@"
880-
881-
#endregion
882-
");
883-
}
884-
885-
/// <summary>
886-
/// Generates operators that express relations between quantities as applied by <see cref="QuantityRelationsParser" />.
887-
/// </summary>
888-
private void GenerateRelationalOperatorsWithFixedUnits()
889-
{
890-
if (!_quantity.Relations.Any()) return;
891-
892-
Writer.WL($@"
893-
#region Relational Operators
894-
");
895-
896-
foreach (QuantityRelation relation in _quantity.Relations)
897-
{
898-
if (relation.Operator == "inverse")
899-
{
900-
Writer.WL($@"
829+
// this is the proposed improvement where the inverse is considered a type of implicit conversion
830+
Writer.WL($@"
901831
/// <summary>Calculates the inverse of this quantity.</summary>
902832
/// <returns>The corresponding inverse quantity, <see cref=""{relation.RightQuantity.Name}""/>.</returns>
903833
public {relation.RightQuantity.Name} Inverse()
904834
{{
905-
return {relation.RightQuantity.Name}.From{relation.RightUnit.PluralName}(QuantityValue.Inverse({relation.LeftUnit.PluralName}));
835+
return UnitConverter.Default.ConvertTo(Value, Unit, {relation.RightQuantity.Name}.Info);
906836
}}
907837
");
838+
}
908839
}
909840
else
910841
{
@@ -993,20 +924,43 @@ private void GenerateEqualityAndComparison()
993924
return left.Value > right.As(left.Unit);
994925
}}
995926
996-
/// <summary>Indicates strict equality of two <see cref=""{_quantity.Name}""/> quantities.</summary>
927+
/// <summary>
928+
/// Determines whether two <see cref=""{_quantity.Name}""/> instances are equal.
929+
/// </summary>
930+
/// <remarks>
931+
/// Equality is evaluated in a unit-aware manner. The right-hand operand is converted to the unit of the left-hand
932+
/// operand and then the underlying numeric values are compared.
933+
/// This means two quantities with numerically equal values but different units will be considered equal.
934+
/// The operator delegates to <see cref=""Equals({_quantity.Name})""/>, which implements this conversion-and-compare logic.
935+
/// </remarks>
997936
public static bool operator ==({_quantity.Name} left, {_quantity.Name} right)
998937
{{
999938
return left.Equals(right);
1000939
}}
1001940
1002-
/// <summary>Indicates strict inequality of two <see cref=""{_quantity.Name}""/> quantities.</summary>
941+
/// <summary>
942+
/// Determines whether two <see cref=""{_quantity.Name}""/> instances are not equal.
943+
/// </summary>
944+
/// <remarks>
945+
/// This operator is the logical negation of <see cref=""operator ==({_quantity.Name},{_quantity.Name})""/>.
946+
/// See that operator (and <see cref=""Equals({_quantity.Name})""/>) for details on how equality is evaluated
947+
/// (i.e., by converting one operand to the other's unit and comparing their numeric values).
948+
/// </remarks>
1003949
public static bool operator !=({_quantity.Name} left, {_quantity.Name} right)
1004950
{{
1005951
return !(left == right);
1006952
}}
1007953
1008954
/// <inheritdoc />
1009-
/// <summary>Indicates strict equality of two <see cref=""{_quantity.Name}""/> quantities.</summary>
955+
/// <summary>
956+
/// Determines whether the specified object is equal to the current <see cref=""{_quantity.Name}""/> instance.
957+
/// </summary>
958+
/// <remarks>
959+
/// Returns <c>false</c> if <paramref name=""obj""/> is <c>null</c> or not a <see cref=""{_quantity.Name}""/>.
960+
/// When <paramref name=""obj""/> is a <see cref=""{_quantity.Name}""/>, this method delegates to
961+
/// <see cref=""Equals({_quantity.Name})""/>, which performs a unit-aware comparison by converting the other
962+
/// instance to this instance's unit before comparing numeric values.
963+
/// </remarks>
1010964
public override bool Equals(object? obj)
1011965
{{
1012966
if (obj is not {_quantity.Name} otherQuantity)
@@ -1016,7 +970,13 @@ public override bool Equals(object? obj)
1016970
}}
1017971
1018972
/// <inheritdoc />
1019-
/// <summary>Indicates strict equality of two <see cref=""{_quantity.Name}""/> quantities.</summary>
973+
/// <summary>
974+
/// Determines whether the current instance is equal to another <see cref=""{_quantity.Name}""/> instance.
975+
/// </summary>
976+
/// <remarks>
977+
/// Comparison is performed by converting <paramref name=""other""/> to this instance's unit and then comparing the underlying numeric values.
978+
/// This makes two quantities equal even when their units differ, provided the converted numeric values are equal.
979+
/// </remarks>
1020980
public bool Equals({_quantity.Name} other)
1021981
{{
1022982
return _value.Equals(other.As(this.Unit));

UnitsNet/GeneratedCode/Quantities/AbsorbedDoseOfIonizingRadiation.g.cs

Lines changed: 33 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)