Problem
The netstandard2.0 and .NET (net8.0+) assemblies in the UnitsNet 6 package expose incompatible public APIs. Some public members exist only in the netstandard2.0 assembly.
A library that targets netstandard2.0 compiles against the netstandard2.0 assembly. When an app targeting .NET 8+ uses that library, NuGet gives the app the .NET assembly, so the library's calls to those members fail at runtime with MissingMethodException.
Apps that target .NET directly aren't affected, since they compile and run against the same assembly. Only netstandard2.0 libraries used from .NET apps are, which is probably why nobody has reported it yet. It would surface once libraries start building on v6.
Repro
A library targeting netstandard2.0 that references UnitsNet:
public static class LibCode
{
public static string GetQuantityName<TQuantity>(TQuantity quantity) where TQuantity : IQuantityOfType<TQuantity>
=> quantity.QuantityInfo.Name;
public static string GetScalingFactor<TQuantity>(TQuantity quantity) where TQuantity : ILogarithmicQuantity<TQuantity>
=> quantity.LogarithmicScalingFactor.ToString();
public static bool AreClose(Temperature a, Temperature b)
=> a.Equals(b, TemperatureDelta.FromKelvins(1));
}
A net10.0 app that references the library and UnitsNet, calling each method, built from master (3bf1ef4):
IQuantityOfType<T>.QuantityInfo: MissingMethodException: Method not found: 'UnitsNet.IQuantityInstanceInfo`1<!0> UnitsNet.IQuantityOfType`1.get_QuantityInfo()'.
ILogarithmicQuantity<T>.LogarithmicScalingFactor: MissingMethodException: Method not found: 'UnitsNet.QuantityValue UnitsNet.ILogarithmicQuantity`1.get_LogarithmicScalingFactor()'.
Temperature.Equals(other, tolerance): MissingMethodException: Method not found: 'Boolean UnitsNet.AffineQuantityExtensions.Equals(UnitsNet.Temperature, UnitsNet.Temperature, UnitsNet.TemperatureDelta)'.
Incompatible APIs
Turning on package validation for UnitsNet reports these as present in netstandard2.0 but missing from net8.0:
IQuantityInfo: public in netstandard2.0, internal on .NET
IQuantityInstanceInfo<TQuantity>: public in netstandard2.0, internal on .NET
IQuantityOfType<TQuantity>.QuantityInfo
AffineQuantityExtensions.Equals(this Temperature, Temperature, TemperatureDelta)
AffineQuantityExtensions.Equals(this Temperature, IQuantity?, TemperatureDelta)
Package validation misses one more, because it matches members by name and doesn't notice a change from instance to static:
ILogarithmicQuantity<TSelf>.LogarithmicScalingFactor: an instance property in netstandard2.0, a static abstract property on .NET
It also reports the static abstract members (Info, Create, From, Zero) and IAdditiveIdentity constraints that exist only on .NET. Those require .NET 7+ and can't exist on netstandard2.0, so they're expected.
Proposal
- Make every public member available on netstandard2.0 exist on all target frameworks. Members that are only there for netstandard2.0 can be
[Obsolete] on .NET, as IQuantity.QuantityInfo already is.
- Turn on package validation for UnitsNet, as UnitsNet.Modular already does, so CI catches future differences. Suppress the static abstract additions with an explanation.
- Decide how to handle
LogarithmicScalingFactor. An interface can't have a static and an instance member with the same name, so either:
- remove the instance member from netstandard2.0, which breaks generic netstandard2.0 code over
ILogarithmicQuantity<T>; or
- add an instance member under a different name on all target frameworks, implemented on .NET as a default interface member that returns
TSelf.LogarithmicScalingFactor.
Problem
The netstandard2.0 and .NET (net8.0+) assemblies in the UnitsNet 6 package expose incompatible public APIs. Some public members exist only in the netstandard2.0 assembly.
A library that targets netstandard2.0 compiles against the netstandard2.0 assembly. When an app targeting .NET 8+ uses that library, NuGet gives the app the .NET assembly, so the library's calls to those members fail at runtime with
MissingMethodException.Apps that target .NET directly aren't affected, since they compile and run against the same assembly. Only netstandard2.0 libraries used from .NET apps are, which is probably why nobody has reported it yet. It would surface once libraries start building on v6.
Repro
A library targeting netstandard2.0 that references UnitsNet:
A net10.0 app that references the library and UnitsNet, calling each method, built from master (3bf1ef4):
Incompatible APIs
Turning on package validation for UnitsNet reports these as present in netstandard2.0 but missing from net8.0:
IQuantityInfo: public in netstandard2.0, internal on .NETIQuantityInstanceInfo<TQuantity>: public in netstandard2.0, internal on .NETIQuantityOfType<TQuantity>.QuantityInfoAffineQuantityExtensions.Equals(this Temperature, Temperature, TemperatureDelta)AffineQuantityExtensions.Equals(this Temperature, IQuantity?, TemperatureDelta)Package validation misses one more, because it matches members by name and doesn't notice a change from instance to static:
ILogarithmicQuantity<TSelf>.LogarithmicScalingFactor: an instance property in netstandard2.0, a static abstract property on .NETIt also reports the static abstract members (
Info,Create,From,Zero) andIAdditiveIdentityconstraints that exist only on .NET. Those require .NET 7+ and can't exist on netstandard2.0, so they're expected.Proposal
[Obsolete]on .NET, asIQuantity.QuantityInfoalready is.LogarithmicScalingFactor. An interface can't have a static and an instance member with the same name, so either:ILogarithmicQuantity<T>; orTSelf.LogarithmicScalingFactor.