Skip to content
Draft
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
26 changes: 1 addition & 25 deletions CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ public double As({_unitEnumName} unit)

Writer.WL( $@"

/// <inheritdoc cref=""IQuantity.As(UnitKey)""/>
/// <inheritdoc cref=""QuantityExtensions.As(IQuantity, UnitKey)""/>
public double As(UnitKey unitKey)
{{
return As(unitKey.ToUnit<{_unitEnumName}>());
Expand Down Expand Up @@ -1129,30 +1129,6 @@ private bool TryToUnit({_unitEnumName} unit, [NotNullWhen(true)] out {_quantity.
}}
");
Writer.WL($@"
#region Explicit implementations

double IQuantity.As(Enum unit)
{{
if (unit is not {_unitEnumName} typedUnit)
throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit));

return As(typedUnit);
}}

/// <inheritdoc />
IQuantity IQuantity.ToUnit(Enum unit)
{{
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(typedUnit, DefaultConversionFunctions);
}}

/// <inheritdoc />
IQuantity<{_unitEnumName}> IQuantity<{_unitEnumName}>.ToUnit({_unitEnumName} unit) => ToUnit(unit);

#endregion

#endregion
");
}
Expand Down
53 changes: 49 additions & 4 deletions UnitsNet.Tests/CustomCode/IQuantityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,60 @@ namespace UnitsNet.Tests;
public partial class IQuantityTests
{
[Fact]
public void As_GivenWrongUnitType_ThrowsArgumentException()
public void As_GivenWrongUnitType_ThrowsUnitNotFoundException()
{
Assert.All(Quantity.Infos.Select(x => x.Zero), quantity => { Assert.Throws<ArgumentException>(() => quantity.As(ComparisonType.Absolute)); });
Assert.All(Quantity.Infos.Select(x => x.Zero), quantity => { Assert.Throws<UnitNotFoundException>(() => quantity.As(ComparisonType.Absolute)); });
}

[Fact]
public void ToUnit_GivenWrongUnitType_ThrowsArgumentException()
public void ToUnit_GivenWrongUnitType_ThrowsUnitNotFoundException()
{
Assert.All(Quantity.Infos.Select(x => x.Zero), quantity => { Assert.Throws<ArgumentException>(() => quantity.ToUnit(ComparisonType.Absolute)); });
Assert.All(Quantity.Infos.Select(x => x.Zero),
quantity => { Assert.Throws<UnitNotFoundException>(() => quantity.ToUnit(ComparisonType.Absolute)); });
}

[Fact]
public void As_InterfaceReferences_ReturnConvertedValue()
{
var mass = Mass.FromKilograms(1);
IQuantity quantity = mass;
IQuantity<MassUnit> typedQuantity = mass;

Assert.Equal(1000, quantity.As(MassUnit.Gram));
Assert.Equal(1000, typedQuantity.As(MassUnit.Gram));
}

[Fact]
public void ToUnit_IQuantityFromNonBaseUnit_ReturnsConvertedQuantity()
{
IQuantity quantity = Length.FromKilometers(1);

IQuantity convertedQuantity = quantity.ToUnit(LengthUnit.Centimeter);

Assert.Equal(100_000, convertedQuantity.Value);
Assert.Equal(LengthUnit.Centimeter, convertedQuantity.Unit);
}

[Fact]
public void ToUnit_GenericConstraintWithCustomConverter_UsesProvidedConverter()
{
var converter = new UnitConverter();
converter.SetConversionFunction<Length>(
LengthUnit.Meter,
LengthUnit.Centimeter,
_ => Length.FromCentimeters(123));

Length convertedQuantity = ConvertToUnit(Length.FromMeters(1), LengthUnit.Centimeter, converter);

Assert.Equal(123, convertedQuantity.Value);
Assert.Equal(LengthUnit.Centimeter, convertedQuantity.Unit);

static TQuantity ConvertToUnit<TQuantity, TUnit>(TQuantity quantity, TUnit unit, UnitConverter unitConverter)
where TQuantity : IQuantity<TQuantity, TUnit>
where TUnit : struct, Enum
{
return quantity.ToUnit(unit, unitConverter);
}
}

[Fact]
Expand Down
22 changes: 0 additions & 22 deletions UnitsNet.Tests/CustomQuantities/HowMuch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ public static HowMuch From(double value, HowMuchUnit unit)
return new HowMuch(value, unit);
}

public double As(HowMuchUnit unit)
{
throw new NotImplementedException();
}

public HowMuchUnit Unit { get; }

public double Value { get; }
Expand Down Expand Up @@ -64,23 +59,6 @@ UnitKey IQuantity.UnitKey
get => UnitKey.ForUnit(Unit);
}

public double As(Enum unit) => Convert.ToDouble(unit);
public double As(UnitKey unitKey)
{
return As(unitKey.ToUnit<HowMuchUnit>());
}

public IQuantity ToUnit(Enum unit)
{
if (unit is HowMuchUnit howMuchUnit) return new HowMuch(As(unit), howMuchUnit);
throw new ArgumentException("Must be of type HowMuchUnit.", nameof(unit));
}

public IQuantity<HowMuchUnit> ToUnit(HowMuchUnit unit)
{
throw new NotImplementedException();
}

public override string ToString()
{
return $"{Value} {Unit}";
Expand Down
136 changes: 133 additions & 3 deletions UnitsNet/Extensions/QuantityExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,142 @@ public static UnitInfo<TQuantity, TUnit> GetUnitInfo<TQuantity, TUnit>(this IQua
#endif
}

/// <inheritdoc cref="IQuantity.As(UnitKey)" />
/// <remarks>This should be using UnitConverter.Default.ConvertValue(quantity, toUnit) </remarks>
private static IQuantity ConvertTo(this UnitConverter converter, IQuantity quantity, UnitKey toUnit)
{
if (quantity.UnitKey == toUnit)
{
return quantity;
}

var targetUnit = (Enum)toUnit;
Type quantityType = quantity.GetType();
if (converter.TryGetConversionFunction(quantityType, quantity.Unit, quantityType, targetUnit, out ConversionFunction? conversionFunction))
{
return conversionFunction(quantity);
}

#pragma warning disable CS0618 // Type or member is obsolete
UnitKey baseUnit = quantity.QuantityInfo.BaseUnitInfo.UnitKey;
#pragma warning restore CS0618 // Type or member is obsolete
if (quantity.UnitKey != baseUnit)
{
IQuantity quantityInBaseUnit = converter.ConvertTo(quantity, baseUnit);
return converter.ConvertTo(quantityInBaseUnit, toUnit);
}

throw new UnitNotFoundException($"Can't convert {quantity.Unit} to {targetUnit}.");
}

/// <summary>
/// Converts a quantity value to the specified unit.
/// </summary>
/// <param name="converter">The converter to use.</param>
/// <param name="quantity">The quantity to convert.</param>
/// <param name="toUnit">The target unit.</param>
/// <returns>The converted value.</returns>
internal static double ConvertValue<TQuantity>(this UnitConverter converter, TQuantity quantity, UnitKey toUnit)
where TQuantity : IQuantity
{
return converter.ConvertTo(quantity, toUnit).Value;
}

/// <summary>
/// Converts a quantity to the specified unit while preserving its concrete type.
/// </summary>
/// <param name="converter">The converter to use.</param>
/// <param name="quantity">The quantity to convert.</param>
/// <param name="toUnit">The target unit.</param>
/// <returns>The converted quantity.</returns>
internal static TQuantity ConvertToUnit<TQuantity>(this UnitConverter converter, TQuantity quantity, UnitKey toUnit)
where TQuantity : IQuantityOfType<TQuantity>
{
return (TQuantity)converter.ConvertTo(quantity, toUnit);
}

/// <summary>
/// Gets the value of a quantity in the specified unit.
/// </summary>
internal static double GetValue<TQuantity>(this TQuantity quantity, UnitKey toUnit)
where TQuantity : IQuantity
{
return quantity.As(toUnit);
return UnitConverter.Default.ConvertValue(quantity, toUnit);
}

/// <summary>
/// Gets the value of a quantity in the specified unit.
/// </summary>
/// <typeparam name="TQuantity">The quantity type.</typeparam>
/// <typeparam name="TUnit">The unit enum type.</typeparam>
/// <param name="quantity">The quantity to convert.</param>
/// <param name="unit">The target unit.</param>
/// <returns>The converted value.</returns>
public static double As<TQuantity, TUnit>(this TQuantity quantity, TUnit unit)
where TQuantity : IQuantity<TQuantity, TUnit>
where TUnit : struct, Enum
{
return UnitConverter.Default.ConvertValue(quantity, UnitKey.ForUnit(unit));
}

/// <summary>
/// Gets the value of a quantity in the specified unit.
/// </summary>
/// <param name="quantity">The quantity to convert.</param>
/// <param name="unit">The target unit.</param>
/// <returns>The converted value.</returns>
public static double As(this IQuantity quantity, UnitKey unit)
{
return UnitConverter.Default.ConvertValue(quantity, unit);
}

/// <summary>
/// Converts a quantity to the specified unit while preserving its concrete type.
/// </summary>
/// <typeparam name="TQuantity">The quantity type.</typeparam>
/// <typeparam name="TUnit">The unit enum type.</typeparam>
/// <param name="quantity">The quantity to convert.</param>
/// <param name="unit">The target unit.</param>
/// <returns>The converted quantity.</returns>
public static TQuantity ToUnit<TQuantity, TUnit>(this TQuantity quantity, TUnit unit)
where TQuantity : IQuantity<TQuantity, TUnit>
where TUnit : struct, Enum
{
return quantity.ToUnit(unit, UnitConverter.Default);
}

/// <inheritdoc cref="ToUnit{TQuantity,TUnit}(TQuantity,TUnit)" />
/// <param name="quantity">The quantity to convert.</param>
/// <param name="unit">The target unit.</param>
/// <param name="unitConverter">The converter to use.</param>
public static TQuantity ToUnit<TQuantity, TUnit>(this TQuantity quantity, TUnit unit, UnitConverter unitConverter)
where TQuantity : IQuantity<TQuantity, TUnit>
where TUnit : struct, Enum
{
if (unitConverter is null) throw new ArgumentNullException(nameof(unitConverter));
return unitConverter.ConvertToUnit(quantity, UnitKey.ForUnit(unit));
}

/// <summary>
/// Converts a quantity to the specified unit.
/// </summary>
/// <param name="quantity">The quantity to convert.</param>
/// <param name="unit">The target unit.</param>
/// <returns>The converted quantity.</returns>
public static IQuantity ToUnit(this IQuantity quantity, UnitKey unit)
{
return UnitConverter.Default.ConvertTo(quantity, unit);
}

/// <summary>
/// Converts a quantity to the specified unit.
/// </summary>
/// <typeparam name="TUnit">The unit enum type.</typeparam>
/// <param name="quantity">The quantity to convert.</param>
/// <param name="unit">The target unit.</param>
/// <returns>The converted quantity.</returns>
public static IQuantity<TUnit> ToUnit<TUnit>(this IQuantity<TUnit> quantity, TUnit unit)
where TUnit : struct, Enum
{
return (IQuantity<TUnit>)UnitConverter.Default.ConvertTo(quantity, UnitKey.ForUnit(unit));
}

/// <summary>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 1 addition & 25 deletions UnitsNet/GeneratedCode/Quantities/Acceleration.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading