Skip to content

IQuantity with QuantityValue as Value type (PoC) #1124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Build/build-functions.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ function Start-Tests {
$projectPaths = @(
"UnitsNet.Tests\UnitsNet.Tests.csproj",
"UnitsNet.NumberExtensions.Tests\UnitsNet.NumberExtensions.Tests.csproj",
"UnitsNet.Serialization.JsonNet.Tests\UnitsNet.Serialization.JsonNet.Tests.csproj",
"UnitsNet.Serialization.JsonNet.CompatibilityTests\UnitsNet.Serialization.JsonNet.CompatibilityTests.csproj"
"UnitsNet.Serialization.JsonNet.Tests\UnitsNet.Serialization.JsonNet.Tests.csproj"
)

# Parent dir must exist before xunit tries to write files to it
Expand Down
2 changes: 1 addition & 1 deletion CodeGen/CodeGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NuGet.Protocol" Version="6.2.1" />
<PackageReference Include="NuGet.Protocol" Version="6.3.0" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.2.0-alpha.19174.3" />
Expand Down
131 changes: 96 additions & 35 deletions CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public QuantityGenerator(Quantity quantity)
throw new ArgumentException($"No unit found with SingularName equal to BaseUnit [{_quantity.BaseUnit}]. This unit must be defined.",
nameof(quantity));

_valueType = quantity.ValueType;
_valueType = "QuantityValue";// quantity.ValueType;
_unitEnumName = $"{quantity.Name}Unit";

BaseDimensions baseDimensions = quantity.BaseDimensions;
Expand Down Expand Up @@ -77,14 +77,14 @@ public partial struct {_quantity.Name} : IQuantity<{_unitEnumName}>, ");
Writer.W("IDecimalQuantity, ");
}

Writer.WL($"IComparable, IComparable<{_quantity.Name}>, IConvertible, IFormattable");
Writer.WL($"IEquatable<{_quantity.Name}>, IComparable, IComparable<{_quantity.Name}>, IConvertible, IFormattable");
Writer.WL($@"
{{
/// <summary>
/// The numeric value this quantity was constructed with.
/// </summary>
[DataMember(Name = ""Value"", Order = 0)]
private readonly {_quantity.ValueType} _value;
private readonly {_valueType} _value;

/// <summary>
/// The unit this quantity was constructed with.
Expand Down Expand Up @@ -176,9 +176,9 @@ private void GenerateInstanceConstructors()
/// <param name=""value"">The numeric value to construct this quantity with.</param>
/// <param name=""unit"">The unit representation to construct this quantity with.</param>
/// <exception cref=""ArgumentException"">If value is NaN or Infinity.</exception>
public {_quantity.Name}({_quantity.ValueType} value, {_unitEnumName} unit)
public {_quantity.Name}({_valueType} value, {_unitEnumName} unit)
{{");
Writer.WL(_quantity.ValueType == "double"
Writer.WL(_valueType == "double"
? @"
_value = Guard.EnsureValidNumber(value, nameof(value));"
: @"
Expand All @@ -203,7 +203,7 @@ private void GenerateInstanceConstructors()
var firstUnitInfo = unitInfos.FirstOrDefault();
");

Writer.WL(_quantity.ValueType == "double"
Writer.WL(_valueType == "double"
? @"
_value = Guard.EnsureValidNumber(value, nameof(value));"
: @"
Expand Down Expand Up @@ -262,15 +262,15 @@ private void GenerateProperties()
public {_valueType} Value => _value;
");

// Need to provide explicit interface implementation for decimal quantities like Information
if (_quantity.ValueType != "double")
Writer.WL(@"
double IQuantity.Value => (double) _value;
Writer.WL($@"
/// <inheritdoc />
{_valueType} IQuantity.Value => _value;
");
// Need to provide explicit interface implementation for decimal quantities like Information
if (_quantity.ValueType == "decimal")
Writer.WL(@"
/// <inheritdoc cref=""IDecimalQuantity.Value""/>
decimal IDecimalQuantity.Value => _value;
decimal IDecimalQuantity.Value => (decimal)_value;
");

Writer.WL($@"
Expand Down Expand Up @@ -306,11 +306,11 @@ private void GenerateConversionProperties()

Writer.WL($@"
/// <summary>
/// Gets a <see cref=""double""/> value of this quantity converted into <see cref=""{_unitEnumName}.{unit.SingularName}""/>
/// Gets the numeric value of this quantity converted into <see cref=""{_unitEnumName}.{unit.SingularName}""/>
/// </summary>");
Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit));
Writer.WL($@"
public double {unit.PluralName} => As({_unitEnumName}.{unit.SingularName});
public {_valueType} {unit.PluralName} => As({_unitEnumName}.{unit.SingularName});
");
}

Expand Down Expand Up @@ -426,7 +426,7 @@ private void GenerateStaticFactoryMethods()
/// <exception cref=""ArgumentException"">If value is NaN or Infinity.</exception>");
Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit));
Writer.WL($@"
public static {_quantity.Name} From{unit.PluralName}(QuantityValue {valueParamName})
public static {_quantity.Name} From{unit.PluralName}({_valueType} {valueParamName})
{{
{_valueType} value = ({_valueType}) {valueParamName};
return new {_quantity.Name}(value, {_unitEnumName}.{unit.SingularName});
Expand Down Expand Up @@ -651,7 +651,7 @@ private void GenerateArithmeticOperators()
}}

/// <summary>Get ratio value from dividing <see cref=""{_quantity.Name}""/> by <see cref=""{_quantity.Name}""/>.</summary>
public static double operator /({_quantity.Name} left, {_quantity.Name} right)
public static {_valueType} operator /({_quantity.Name} left, {_quantity.Name} right)
{{
return left.{_baseUnit.PluralName} / right.{_baseUnit.PluralName};
}}
Expand Down Expand Up @@ -679,15 +679,15 @@ private void GenerateLogarithmicArithmeticOperators()
{{
// Logarithmic addition
// Formula: {x} * log10(10^(x/{x}) + 10^(y/{x}))
return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, left.Value/{x}) + Math.Pow(10, right.GetValueAs(left.Unit)/{x})), left.Unit);
return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, (double)left.Value/{x}) + Math.Pow(10, (double)right.GetValueAs(left.Unit)/{x})), left.Unit);
}}

/// <summary>Get <see cref=""{_quantity.Name}""/> from logarithmic subtraction of two <see cref=""{_quantity.Name}""/>.</summary>
public static {_quantity.Name} operator -({_quantity.Name} left, {_quantity.Name} right)
{{
// Logarithmic subtraction
// Formula: {x} * log10(10^(x/{x}) - 10^(y/{x}))
return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, left.Value/{x}) - Math.Pow(10, right.GetValueAs(left.Unit)/{x})), left.Unit);
return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, (double)left.Value/{x}) - Math.Pow(10, (double)right.GetValueAs(left.Unit)/{x})), left.Unit);
}}

/// <summary>Get <see cref=""{_quantity.Name}""/> from logarithmic multiplication of value and <see cref=""{_quantity.Name}""/>.</summary>
Expand Down Expand Up @@ -751,6 +751,19 @@ private void GenerateEqualityAndComparison()
return left.Value > right.GetValueAs(left.Unit);
}}

/// <summary>Returns true if exactly equal.</summary>
/// <remarks>Consider using <see cref=""Equals({_quantity.Name}, {_valueType}, ComparisonType)""/> for safely comparing floating point values.</remarks>
public static bool operator ==({_quantity.Name} left, {_quantity.Name} right)
{{
return left.Equals(right);
}}
/// <summary>Returns true if not exactly equal.</summary>
/// <remarks>Consider using <see cref=""Equals({_quantity.Name}, {_valueType}, ComparisonType)""/> for safely comparing floating point values.</remarks>
public static bool operator !=({_quantity.Name} left, {_quantity.Name} right)
{{
return !(left == right);
}}

/// <inheritdoc />
public int CompareTo(object obj)
{{
Expand All @@ -763,7 +776,29 @@ public int CompareTo(object obj)
/// <inheritdoc />
public int CompareTo({_quantity.Name} other)
{{
return _value.CompareTo(other.GetValueAs(this.Unit));
var asFirstUnit = other.GetValueAs(this.Unit);
var asSecondUnit = GetValueAs(other.Unit);
return (_value.CompareTo(asFirstUnit) - other.Value.CompareTo(asSecondUnit)) / 2;
}}

/// <inheritdoc />
/// <remarks>Consider using <see cref=""Equals({_quantity.Name}, {_valueType}, ComparisonType)""/> for safely comparing floating point values.</remarks>
public override bool Equals(object obj)
{{
if (obj is null || !(obj is {_quantity.Name} obj{_quantity.Name}))
return false;
return Equals(obj{_quantity.Name});
}}

/// <inheritdoc />
/// <remarks>Consider using <see cref=""Equals({_quantity.Name}, {_valueType}, ComparisonType)""/> for safely comparing floating point values.</remarks>
public bool Equals({_quantity.Name} other)
{{
if (Value.IsDecimal)
return other.Value.Equals(this.GetValueAs(other.Unit));
if (other.Value.IsDecimal)
return Value.Equals(other.GetValueAs(this.Unit));
return this.Unit == other.Unit && this.Value.Equals(other.Value);
}}

/// <summary>
Expand Down Expand Up @@ -806,13 +841,13 @@ public int CompareTo({_quantity.Name} other)
/// <param name=""tolerance"">The absolute or relative tolerance value. Must be greater than or equal to 0.</param>
/// <param name=""comparisonType"">The comparison type: either relative or absolute.</param>
/// <returns>True if the absolute difference between the two values is not greater than the specified relative or absolute tolerance.</returns>
public bool Equals({_quantity.Name} other, double tolerance, ComparisonType comparisonType)
public bool Equals({_quantity.Name} other, {_valueType} tolerance, ComparisonType comparisonType)
{{
if (tolerance < 0)
throw new ArgumentOutOfRangeException(""tolerance"", ""Tolerance must be greater than or equal to 0."");

double thisValue = (double)this.Value;
double otherValueInThisUnits = other.As(this.Unit);
{_valueType} thisValue = this.Value;
{_valueType} otherValueInThisUnits = other.As(this.Unit);

return UnitsNet.Comparison.Equals(thisValue, otherValueInThisUnits, tolerance, comparisonType);
}}
Expand All @@ -823,7 +858,7 @@ public bool Equals({_quantity.Name} other, double tolerance, ComparisonType comp
/// <returns>A hash code for the current {_quantity.Name}.</returns>
public override int GetHashCode()
{{
return new {{ Info.Name, Value, Unit }}.GetHashCode();
return Info.Name.GetHashCode();
}}

#endregion
Expand All @@ -839,17 +874,30 @@ private void GenerateConversionMethods()
/// Convert to the unit representation <paramref name=""unit"" />.
/// </summary>
/// <returns>Value converted to the specified unit.</returns>
public double As({_unitEnumName} unit)
public {_valueType} As({_unitEnumName} unit)
{{
if (Unit == unit)
return Convert.ToDouble(Value);
if(Unit == unit)
return Value;

var converted = GetValueAs(unit);
return Convert.ToDouble(converted);
return GetValueAs(unit);
}}
");

if (_valueType == "decimal")
{
Writer.WL($@"

{_valueType} IQuantity<{_unitEnumName}>.As({_unitEnumName} unit)
{{
return ({_valueType})As(unit);
}}
");
}

Writer.WL($@"

/// <inheritdoc cref=""IQuantity.As(UnitSystem)""/>
public double As(UnitSystem unitSystem)
public {_valueType} As(UnitSystem unitSystem)
{{
if (unitSystem is null)
throw new ArgumentNullException(nameof(unitSystem));
Expand All @@ -862,14 +910,27 @@ public double As(UnitSystem unitSystem)

return As(firstUnitInfo.Value);
}}
");

if (_valueType == "decimal")
{
Writer.WL($@"
/// <inheritdoc cref=""IQuantity.As(UnitSystem)""/>
{_valueType} IQuantity.As(UnitSystem unitSystem)
{{
return ({_valueType})As(unitSystem);
}}
");
}

Writer.WL($@"
/// <inheritdoc />
double IQuantity.As(Enum unit)
{_valueType} IQuantity.As(Enum unit)
{{
if (!(unit is {_unitEnumName} unitAs{_unitEnumName}))
if (!(unit is {_unitEnumName} typedUnit))
throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit));

return As(unitAs{_unitEnumName});
return ({_valueType})As(typedUnit);
}}

/// <summary>
Expand Down Expand Up @@ -901,25 +962,25 @@ double IQuantity.As(Enum unit)
var converted = conversionFunction(this);
return ({_quantity.Name})converted;
}}
else if (Unit != BaseUnit)
else if (Enum.IsDefined(typeof({_unitEnumName}), unit))
{{
// Direct conversion to requested unit NOT found. Convert to BaseUnit, and then from BaseUnit to requested unit.
var inBaseUnits = ToUnit(BaseUnit);
return inBaseUnits.ToUnit(unit);
}}
else
{{
throw new NotImplementedException($""Can not convert {{Unit}} to {{unit}}."");
throw new NotSupportedException($""Can not convert {{Unit}} to {{unit}}."");
}}
}}

/// <inheritdoc />
IQuantity IQuantity.ToUnit(Enum unit)
{{
if (!(unit is {_unitEnumName} unitAs{_unitEnumName}))
if (!(unit is {_unitEnumName} typedUnit))
throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit));

return ToUnit(unitAs{_unitEnumName}, DefaultConversionFunctions);
return ToUnit(typedUnit, DefaultConversionFunctions);
}}

/// <inheritdoc cref=""IQuantity.ToUnit(UnitSystem)""/>
Expand Down
Loading