Skip to content

QuantityValue implemented as a fractional number 🐲 #1544

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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion Build/build-functions.psm1
Original file line number Diff line number Diff line change
@@ -63,7 +63,8 @@ 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.Tests\UnitsNet.Serialization.JsonNet.Tests.csproj",
"UnitsNet.Serialization.SystemTextJson.Tests\UnitsNet.Serialization.SystemTextJson.csproj"
)

# Parent dir must exist before xunit tries to write files to it
@@ -102,6 +103,7 @@ function Start-PackNugets([boolean] $IncludeNanoFramework = $false) {
$projectPaths = @(
"UnitsNet\UnitsNet.csproj",
"UnitsNet.Serialization.JsonNet\UnitsNet.Serialization.JsonNet.csproj",
"UnitsNet.Serialization.SystemTextJson\UnitsNet.Serialization.SystemTextJson.csproj",
"UnitsNet.NumberExtensions\UnitsNet.NumberExtensions.csproj"
)

3 changes: 2 additions & 1 deletion CodeGen/CodeGen.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
@@ -10,6 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Fractions" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="NuGet.Protocol" />
<PackageReference Include="Serilog" />
6 changes: 3 additions & 3 deletions CodeGen/Generators/UnitsNetGen/NumberExtensionsGenerator.cs
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ public static class NumberTo{_quantityName}Extensions
continue;

Writer.WL(2, $@"
/// <inheritdoc cref=""{_quantityName}.From{unit.PluralName}(double)"" />");
/// <inheritdoc cref=""{_quantityName}.From{unit.PluralName}(QuantityValue)"" />");

// Include obsolete text from the quantity per extension method, to make it visible when the class is not explicitly referenced in code.
Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit.ObsoleteText ?? _quantity.ObsoleteText));
@@ -49,10 +49,10 @@ public static class NumberTo{_quantityName}Extensions
where T : notnull
#if NET7_0_OR_GREATER
, INumber<T>
=> {_quantityName}.From{unit.PluralName}(double.CreateChecked(value));
=> {_quantityName}.From{unit.PluralName}(QuantityValue.CreateChecked(value));
#else
, IConvertible
=> {_quantityName}.From{unit.PluralName}(value.ToDouble(null));
=> {_quantityName}.From{unit.PluralName}(value.ToQuantityValue());
#endif
");
}
988 changes: 431 additions & 557 deletions CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs

Large diffs are not rendered by default.

128 changes: 35 additions & 93 deletions CodeGen/Generators/UnitsNetGen/StaticQuantityGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CodeGen.Helpers;
using System.Collections.Generic;
using System.Linq;
using CodeGen.JsonTypes;

namespace CodeGen.Generators.UnitsNetGen
@@ -16,123 +17,64 @@ public string Generate()
{
Writer.WL(GeneratedFileHeader);
Writer.WL(@"
using System;
using System.Globalization;
using UnitsNet.Units;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

#nullable enable

namespace UnitsNet
namespace UnitsNet;

/// <summary>
/// Dynamically parse or construct quantities when types are only known at runtime.
/// </summary>
public partial class Quantity
{
/// <summary>
/// Dynamically parse or construct quantities when types are only known at runtime.
/// Serves as a repository for predefined quantity conversion mappings, facilitating the automatic generation and retrieval of unit conversions in the UnitsNet library.
/// </summary>
public partial class Quantity
internal static class Provider
{
/// <summary>
/// All QuantityInfo instances mapped by quantity name that are present in UnitsNet by default.
/// All QuantityInfo instances that are present in UnitsNet by default.
/// </summary>
public static readonly IDictionary<string, QuantityInfo> ByName = new Dictionary<string, QuantityInfo>
internal static IReadOnlyList<QuantityInfo> DefaultQuantities => new QuantityInfo[]
{");
foreach (var quantity in _quantities)
Writer.WL($@"
{{ ""{quantity.Name}"", {quantity.Name}.Info }},");
{quantity.Name}.Info,");
Writer.WL(@"
};

/// <summary>
/// Dynamically constructs a quantity of the given <see cref=""QuantityInfo""/> with the value in the quantity's base units.
/// All implicit quantity conversions that exist by default.
/// </summary>
/// <param name=""quantityInfo"">The <see cref=""QuantityInfo""/> of the quantity to create.</param>
/// <param name=""value"">The value to construct the quantity with.</param>
/// <returns>The created quantity.</returns>
public static IQuantity FromQuantityInfo(QuantityInfo quantityInfo, double value)
{
return quantityInfo.Name switch
{");
foreach (var quantity in _quantities)
{
var quantityName = quantity.Name;
internal static readonly IReadOnlyList<QuantityConversionMapping> DefaultConversions = new QuantityConversionMapping[]
{");
foreach (var quantityRelation in _quantities.SelectMany(quantity => quantity.Relations.Where(x => x.Operator == "inverse")).Distinct(new CumulativeRelationshipEqualityComparer()).OrderBy(relation => relation.LeftQuantity.Name))
Writer.WL($@"
""{quantityName}"" => {quantityName}.From(value, {quantityName}.BaseUnit),");
}

new (typeof({quantityRelation.LeftQuantity.Name}), typeof({quantityRelation.RightQuantity.Name})),");
Writer.WL(@"
_ => throw new ArgumentException($""{quantityInfo.Name} is not a supported quantity."")
};
};
}
}");
return Writer.ToString();
}
}

/// <summary>
/// Try to dynamically construct a quantity.
/// </summary>
/// <param name=""value"">Numeric value.</param>
/// <param name=""unit"">Unit enum value.</param>
/// <param name=""quantity"">The resulting quantity if successful, otherwise <c>default</c>.</param>
/// <returns><c>True</c> if successful with <paramref name=""quantity""/> assigned the value, otherwise <c>false</c>.</returns>
public static bool TryFrom(double value, Enum? unit, [NotNullWhen(true)] out IQuantity? quantity)
internal class CumulativeRelationshipEqualityComparer: IEqualityComparer<QuantityRelation>{
public bool Equals(QuantityRelation? x, QuantityRelation? y)
{
quantity = unit switch
{");
foreach (var quantity in _quantities)
{
var quantityName = quantity.Name;
var unitTypeName = $"{quantityName}Unit";
var unitValue = unitTypeName.ToCamelCase();
Writer.WL($@"
{unitTypeName} {unitValue} => {quantityName}.From(value, {unitValue}),");
}

Writer.WL(@"
_ => null
};

return quantity is not null;
if (ReferenceEquals(x, y)) return true;
if (x is null) return false;
if (y is null) return false;
if (x.GetType() != y.GetType()) return false;
return
x.ResultQuantity == y.ResultQuantity && (
(x.LeftQuantity.Equals(y.LeftQuantity) && x.RightQuantity.Equals(y.RightQuantity))
|| (x.LeftQuantity.Equals(y.RightQuantity) && x.RightQuantity.Equals(y.LeftQuantity)));
}

/// <summary>
/// Try to dynamically parse a quantity string representation.
/// </summary>
/// <param name=""formatProvider"">The format provider to use for lookup. Defaults to <see cref=""CultureInfo.CurrentCulture"" /> if null.</param>
/// <param name=""quantityType"">Type of quantity, such as <see cref=""Length""/>.</param>
/// <param name=""quantityString"">Quantity string representation, such as ""1.5 kg"". Must be compatible with given quantity type.</param>
/// <param name=""quantity"">The resulting quantity if successful, otherwise <c>default</c>.</param>
/// <returns>The parsed quantity.</returns>
public static bool TryParse(IFormatProvider? formatProvider, Type quantityType, [NotNullWhen(true)] string? quantityString, [NotNullWhen(true)] out IQuantity? quantity)
public int GetHashCode(QuantityRelation obj)
{
quantity = default(IQuantity);

if (!typeof(IQuantity).IsAssignableFrom(quantityType))
return false;

var parser = UnitsNetSetup.Default.QuantityParser;

return quantityType switch
{");
foreach (var quantity in _quantities)
{
var quantityName = quantity.Name;
Writer.WL($@"
Type _ when quantityType == typeof({quantityName}) => parser.TryParse<{quantityName}, {quantityName}Unit>(quantityString, formatProvider, {quantityName}.From, out quantity),");
}

Writer.WL(@"
_ => false
};
}

internal static IEnumerable<Type> GetQuantityTypes()
{");
foreach (var quantity in _quantities)
Writer.WL($@"
yield return typeof({quantity.Name});");
Writer.WL(@"
}
}
}");
return Writer.ToString();
return obj.LeftQuantity.GetHashCode() ^ obj.RightQuantity.GetHashCode();
}
}
}
Loading