Skip to content

Commit 61d5810

Browse files
angularsenclaude
andcommitted
Make typed GetUnitInfo overload return UnitInfo<TQuantity, TUnit>
Replaces the IQuantity<TUnit> -> UnitInfo<TUnit> overload with IQuantity<TQuantity, TUnit> -> UnitInfo<TQuantity, TUnit>, so the typed extension returns the most specific UnitInfo shape when the caller has a concretely-typed quantity (e.g. Length). Behavior: - Length q; q.GetUnitInfo() -> UnitInfo<Length, LengthUnit> - IQuantity q; q.GetUnitInfo() -> UnitInfo (non-generic fallback) - IQuantity<TUnit> q; q.GetUnitInfo() -> UnitInfo (non-generic fallback) The IQuantity<TUnit> reference case loses its typed UnitInfo<TUnit> return, but that scenario is uncommon compared to the concretely-typed receiver case which now returns the richer UnitInfo<TQuantity, TUnit>. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8a9d731 commit 61d5810

2 files changed

Lines changed: 35 additions & 6 deletions

File tree

UnitsNet.Tests/CustomCode/IQuantityTests.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,31 @@ public void GetUnitInfo_Zero_ReturnsBaseUnitInfo()
8282
}
8383

8484
[Fact]
85-
public void GetUnitInfo_TypedQuantity_ReturnsTypedUnitInfo()
85+
public void GetUnitInfo_ConcreteQuantity_ReturnsFullyTypedUnitInfo()
8686
{
87-
IQuantity<LengthUnit> quantity = new Length(3.0, LengthUnit.Centimeter);
87+
var quantity = new Length(3.0, LengthUnit.Centimeter);
8888

89-
UnitInfo<LengthUnit> unitInfo = quantity.GetUnitInfo();
89+
// Overload resolution picks GetUnitInfo<TQuantity, TUnit> for the concrete struct receiver,
90+
// returning the most specific UnitInfo<Length, LengthUnit>.
91+
UnitInfo<Length, LengthUnit> unitInfo = quantity.GetUnitInfo();
9092

9193
Assert.Equal(LengthUnit.Centimeter, unitInfo.Value);
9294
Assert.Equal(nameof(LengthUnit.Centimeter), unitInfo.Name);
9395
}
9496

97+
[Fact]
98+
public void GetUnitInfo_TypedQuantityReference_FallsBackToNonGeneric()
99+
{
100+
IQuantity<LengthUnit> quantity = new Length(3.0, LengthUnit.Centimeter);
101+
102+
// The IQuantity<TUnit> reference does not satisfy the IQuantity<TSelf, TUnit> constraint
103+
// (TSelf would be IQuantity<MassUnit>), so resolution falls back to GetUnitInfo(IQuantity).
104+
UnitInfo unitInfo = quantity.GetUnitInfo();
105+
106+
Assert.Equal(LengthUnit.Centimeter, ((UnitInfo<LengthUnit>)unitInfo).Value);
107+
Assert.Equal(nameof(LengthUnit.Centimeter), unitInfo.Name);
108+
}
109+
95110
[Fact]
96111
public void GetUnitInfo_MatchesUnit()
97112
{

UnitsNet/Extensions/QuantityExtensions.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ public static class QuantityExtensions
1414
/// <summary>
1515
/// Gets the <see cref="UnitInfo"/> for the unit this quantity was constructed with.
1616
/// </summary>
17+
/// <remarks>
18+
/// Picked by overload resolution for callers that only have an <see cref="IQuantity"/> reference.
19+
/// Concretely-typed callers (e.g. a <c>Mass</c> receiver) bind to the
20+
/// <see cref="GetUnitInfo{TQuantity,TUnit}(IQuantity{TQuantity,TUnit})"/> overload and get the
21+
/// more specific <see cref="UnitInfo{TQuantity,TUnit}"/> return.
22+
/// </remarks>
1723
/// <param name="quantity">The quantity.</param>
1824
/// <returns>The <see cref="UnitInfo"/> for the quantity's unit.</returns>
1925
public static UnitInfo GetUnitInfo(this IQuantity quantity)
@@ -22,12 +28,20 @@ public static UnitInfo GetUnitInfo(this IQuantity quantity)
2228
}
2329

2430
/// <summary>
25-
/// Gets the <see cref="UnitInfo{TUnit}"/> for the unit this quantity was constructed with.
31+
/// Gets the <see cref="UnitInfo{TQuantity,TUnit}"/> for the unit this quantity was constructed with.
2632
/// </summary>
33+
/// <remarks>
34+
/// Picked by overload resolution for concretely-typed receivers (e.g. <c>Mass</c>) where C# can
35+
/// infer both <typeparamref name="TQuantity"/> and <typeparamref name="TUnit"/> from the receiver's
36+
/// <see cref="IQuantity{TSelf,TUnit}"/> implementation. Callers with only an <see cref="IQuantity"/>
37+
/// reference fall back to the non-generic <see cref="GetUnitInfo(IQuantity)"/> overload.
38+
/// </remarks>
39+
/// <typeparam name="TQuantity">The quantity type.</typeparam>
2740
/// <typeparam name="TUnit">The unit enum type.</typeparam>
2841
/// <param name="quantity">The quantity.</param>
29-
/// <returns>The <see cref="UnitInfo{TUnit}"/> for the quantity's unit.</returns>
30-
public static UnitInfo<TUnit> GetUnitInfo<TUnit>(this IQuantity<TUnit> quantity)
42+
/// <returns>The <see cref="UnitInfo{TQuantity,TUnit}"/> for the quantity's unit.</returns>
43+
public static UnitInfo<TQuantity, TUnit> GetUnitInfo<TQuantity, TUnit>(this IQuantity<TQuantity, TUnit> quantity)
44+
where TQuantity : IQuantity<TQuantity, TUnit>
3145
where TUnit : struct, Enum
3246
{
3347
return quantity.QuantityInfo[quantity.Unit];

0 commit comments

Comments
 (0)