Skip to content
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

Creating generic version of structs (WIP) #714

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

tmilnthorp
Copy link
Collaborator

@tmilnthorp tmilnthorp commented Oct 22, 2019

Initial pass. Still a WIP. Any help appreciated!

Needs some additional changes (NaN handling, etc.).

@tmilnthorp tmilnthorp changed the title Creating generic version of structs Creating generic version of structs (WIP) Oct 23, 2019
@stale
Copy link

stale bot commented Dec 22, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Dec 22, 2019
@stale stale bot closed this Dec 29, 2019
@angularsen angularsen added pinned Issues that should not be auto-closed due to inactivity. and removed wontfix labels Dec 29, 2019
@angularsen
Copy link
Owner

Bad bot

@lipchev
Copy link
Collaborator

lipchev commented Apr 5, 2020

I have a few questions as to what the expectations are for the generic structs:

  1. Suppose we get this to work on the structs - do we keep the non-generic structs? My guess is that you mean to spawn another nuget- am I correct? If so- how many spawns? If the answer is one per type, then do we expect to have the type parameters erased in each one? Maybe replaced by some naming convention (e.g. ExactMass, PreciseDensity)?
  2. Do we aim at supporting the integer based quantities? Obviously I wouldn't expect to say Mass< int >.FromGrams(1).ToUnit(Killogram) and get a meaningful result (unless the result is promoted, but that might be a little awkward)- on the other hand my guess is that I would be fine in going down the scale (as long as the scaling factor is integer-based - like with SI)
  3. If the answer to (2) is yes, the I guess we could easily define the integer operations as operating on the smaller of the two scales- e.g. 1 g + 1 mg = 1001 mg
  4. Interoperability- do we intend to support operations between the different value domains- my guess would be yes- so am I right then in assuming that implicit/explicit casting is in the books?
  5. If the answer to (4) is yes, then have you considered the convention to use? Here are two options (as far as I can see) that could make sense (ideally both could find their place, under some differently named classes/nugets):
  • strictly following the .net implicit/explicit casting rules : (implicit int->double/decimal), explicit otherwise:
Mass < double > doubleMass = new Mass < int > (1, Gram);
Mass < decimal > decimalMass = new Mass < int > (1, Gram);
Mass < int > integerMass = ( Mass < int > ) doubleMass;
decimalMass = integerMass;
decimalMass = ( Mass < decimal >) doubleMass ;
doubleMass = new Mass (1, Gram)  // if we decide to keep the non-generic structs
  • assuming that double range is a subset of the decimal range - something that I would argue is a fair assumption, given that we have the Units to scale us back from the problems with representing huge values: not sure if this is actually out of range for the decimals- but consider something like astronomical units and their scaled back version that uses the "earth masses" unit.
    Lets assume that PreciseMass is an alias/extension(yes, I know structs etc.) of the Mass < decimal > and the Mass be the Mass < double > (or simply the normal Mass) - then wouldn't it be wonderful if you could write:
PreciseMass preciseMass = Mass.FromGrams(1); // implicit cast from either Mass < double > or Mass
preciseMass += Mass.FromGrams(1); // no problem
Mass doubleMass = (Mass) preciseMass ; // possible loss of precision (as before)

  1. If we're ok with (3), (4) & (5) - then my guess is that it would make sense to have the full promotion scheme in place- such as
Mass < int > exactMass =  new Mass < int > (1, Gram)
Mass < double > promotedToDouble = exactMass * 2.0d;
Mass < decimal > promotedToDecimal = exactMass / 2.0m;
PreciseMass = promotedToDouble / 2.0d;  // in the case of (5b)

  1. In case the structs give as too much trouble / and or decide to not have X * 3 (int, double, decimal) classes generated in the code-base- I have working a POC (3-4 classes referencing the current UnitsNet release via nuget- that is- no source code modifications) which does pretty much the same thing (as per 5b), by using a generic abstract wrapper QuantityBase< TUnit, TValue, TQuantity > - and implementing the IQuantity<TUnit, TValue> interface by providing implementations for each of the 3 types of interest (with all necessary implicit/explicit/operator definitions):
  • Quantity < TUnit, TQuantity > : QuantityBase< TUnit, double, TQuantity >
  • ExactQuantity < TUnit, TQuantity > : QuantityBase < TUnit, int, TQuantity >
  • PreciseQuantity < TUnit, TQuantity > : QuantityBase < TUnit, decimal, TQuantity >
    Additionally I've re-implemented the UnitMath classes (one for each domain, implementing only the relevant operations- e.g. ExactMath has Sum but no Average/Std/RSS etc. )
    I haven't run any benchmarks on the PreciseMath module - that uses decimal Sqrt (not Math.Sqrt) 😲 but I imagine it's gonna be pretty abysmal. However it should alleviate some of the stress related to the possible loss of precision when calculating stuff like uncertainties (tons of Sqrt/Sqr on quantities very close to zero).
    In any case- most people (including me) mostly care about the dreaded infinite fraction problem :
 [TestMethod]
        public void Assert_PreciseQuantity_EliminatesRoundingError()
        {
            var doubleMass = Mass.FromGrams(0.001);

            Assert.AreEqual(0.001, doubleMass.As(MassUnit.Gram));
            Assert.AreNotEqual(1000, doubleMass.As(MassUnit.Microgram));
            Assert.AreNotEqual(100, (doubleMass * 0.1).As(MassUnit.Microgram));
            Assert.AreNotEqual(1000, (doubleMass / 0.000001).As(MassUnit.Gram));

            PreciseQuantity<MassUnit, Mass> mass = doubleMass;

            Assert.AreEqual(0.001, mass.As(MassUnit.Gram));
            Assert.AreEqual(0.001m, mass.GetValue(MassUnit.Gram));
            Assert.AreEqual(0.001m, mass[MassUnit.Gram]);

            Assert.AreNotEqual(1000, mass.As(MassUnit.Microgram));
            Assert.AreEqual(1000, mass[MassUnit.Microgram]);

            Assert.AreEqual(100, (mass * 0.1m).GetValue(MassUnit.Microgram));
            Assert.AreEqual(1000, (mass / 0.000001m)[MassUnit.Gram]);
            Assert.AreEqual(1000.0, (mass / 0.000001m).As(MassUnit.Gram));
        }
  1. The limitations of the implementation presented in (7) however come into play when multiplications/divisions with other Quantities are involved - in this case I'm limited to either re-implementing them all on my own (say inside a class such as BalanceReadout : ExactQuantity< Mass >) - or alternatively fail-back to the operators of the underlying TQuantity (Mass in this case) - that result in normal (structs) that I can again convert back to Precise (implicitly).
    TBH I don't see either of those tricks as a valid solution (I mean, I think this is more or less the limit of what can be achieved with no source-modifications/ auto-generation). IMAHO- the over-arching problem can be defined as "how do we automatically generate the operators" {TResult, TLeft, TRight} - such that they are consistent with all of the described/desired use-cases- for more on this have a look at my other short-book on the subject ( Redesign UnitSystem to support non-SI systems and configurable default units #709 )
  2. Finally- a word on the usefulness (in my opinion) of the ExactQuantities (int) : consider the case where we're recording the output of a device (say an analytical balance) - we known the resolution of the received output (say it is 0.01mg) - I am very happy to represent a given result, say 2 mg as 2000 ug recorded from a device with 10 ug resolution (not that we could just as well have a resolution that is not a exact power of 10 - e.g. 500 ug resolution). This alleviates the need for rounding before storing to db / displaying on a report. As for the operations - well who doesn't want to say the the NetWeight = GrossWeight - MassContainer (all of them represented as ExactMasses)
  3. I'm not gonna even bother re-reading the whole thing at this point- so I guess an apology is in order, for anyone else who's gotten this far.. :)

@angularsen
Copy link
Owner

angularsen commented Apr 5, 2020

First, wow, nice brain dump :-)

  1. Do we aim at supporting the integer based quantities?

I don't think so? I can't think of any good reason why we should. float, double and decimal might make sense.

  1. Interoperability- do we intend to support operations between the different value domains?

Yes, I guess.
Your proposed casting scheme following .NET implicit/explicit casts between number types make sense to me.

PreciseMass

Did I understand right that you want to this naming?

  • PreciseMass == Mass<decimal>
  • Mass == Mass<double>

How about Mass<float> then?

If we introduce separate nugets for the different numeric types, then another option is to introduce UnitsNet.DecimalType.Mass and UnitsNet.FloatType.Mass.
Not sure if I like it, but it is an alternative.

You pretty much lost me at points 7-8 and onwards 😆 This all sounds very complicated on the first read-through.

It's been too long since I last had my brain on this subject, so I keep forgetting all the pitfalls and pros/cons of solutions and ideas in the previous discussions.

For one, this PR absolutely need a summary in its description that explains why this is a good idea, what it solves, who cares, what the downsides are - and it needs to be short and concise 😄 If you intend to follow up further on this, I'd start with this summary.

@lipchev
Copy link
Collaborator

lipchev commented Apr 5, 2020

I don't think so? I can't think of any good reason why we should. float, double and decimal` might make sense.

I knew I should have started with (9) :)

How aboutMass<float>then?

I don't much care for floats anyway- do you see a reason (other than ~completeness) why we should have it at all?

If we introduce separate nugets for the different numeric types, then another option is to introduce UnitsNet.DecimalType.Mass and UnitsNet.FloatType.Mass.
Not sure if I like it, but it is an alternative.

Yes, that's what I initially thought as well- but I think the decision on this depends on the casting scheme we select- sticking with the strict casting rules will protect us from having a var that's not from the expected namespace.
On the other hand- the Precise/Exact naming was intended to emphasize the convention that is applied for the implicit casting to decimal (Precise implying that we have more decimal places- instead of absolute range). I still can't find a suitable name that I'd use for an alternative Quantity<decimal> that respects the strict casting rules. I mean if we're gonna have 3 nugets, might as well have 4 right? :)
Besides- people who are brave enough to mix in domains- should be comfortable creating an alias like PreciseMass = UnitsNet.DecimalType.Mass (or whatever the syntax was).
In that regard- the same could be said for the generic-structs, but here I'm not 100% people would be happy - does xaml support those properly nowadays? I still tend to strip down generic parameters before creating a DataTemplate.

In summary- my intuition tells me that in order to accomplish this task- we first have to ensure the operator generation- since there is no base class where contributors can add operators that work on all domains- we would have to either force them to create them for each domain (not acceptable) or figure out a way to generate them from whatever is given in the JSON (I haven't yet touched on this in #709 but the rest of the operator generation ideas are there).

@lipchev
Copy link
Collaborator

lipchev commented Apr 9, 2020

Lol, I checked the Earth & Solar masses- turns out they are pretty big- ~6e24 & ~2e30 respectively- so I guess decimal outOfRange = oneSolarMass.As(MassUnit.Kilograms) is a dead end...It follows that decimal noLoveEither = oneSolarMass.As(MassUnit.EarthMasses) is also not possible (conversion via base unit). I'm starting to feel like this has already been discussed before and I'm just late to the party :)

@tmilnthorp
Copy link
Collaborator Author

tmilnthorp commented May 23, 2020

I am not sure why we would have any more packages. I would just expect consumers to define a quantity as say Length<double>. Keeping the current genericless classes would be nice. If only C# had a using/typedef equivalent!

@ebfortin
Copy link
Contributor

Maybe at the end of porting the whole thing to generics, the current genericless classes should inherit from the SomeUnit one. And in theory every users of the types fall back on their feet.

@tmilnthorp
Copy link
Collaborator Author

You can't derive from a class of the same name. Would probably have to change the name of the generic type somehow to keep the current naming.

@lipchev
Copy link
Collaborator

lipchev commented May 23, 2020

Maybe I misunderstand but you can technically derive from the same name- as long as the namespace is different.
However I think this is besides the point- as long as we are still in the struct realm.

Again I apologize for the 10 point comment- although the points still appear valid (even after a later re-read)- the most important question in my view is found my last comment about the range & the astronomical units. Could you comment on it please? @tmilnthorp @angularsen

@tmilnthorp
Copy link
Collaborator Author

tmilnthorp commented May 24, 2020

Sorry I haven't had a chance to look through them yet. In those cases you should receive an OverflowException

@lipchev
Copy link
Collaborator

lipchev commented May 24, 2020

Yeah, I guess there is no way around it- other than going for some BigDecimal implementation (there were talks about Rationals before- so given the compiled lambdas flexibility - this might not be an outlandish idea).
Otherwise - we should probably have special handling for the overflowing units- we couldn't for instance just "copy paste" the tests with the new precision.

@tmilnthorp
Copy link
Collaborator Author

Yea exactly, the generic type is pretty powerful. There's a System.Numerics.BigInteger you could put in there for example. With solar mass in kilograms, you may decide that the decimal places just aren't important anymore :)

@ebfortin
Copy link
Contributor

I did a double double implementation. Maybe it could be added to UnitsNet.

@angularsen
Copy link
Owner

angularsen commented May 27, 2020

Hi guys, I have a hard time finding the time to help move this forward, but one thing I'm still missing from this discussion is; although it's fun to contemplate what we can do, we also need to consider what we should do. Why are we adding this?

For my simple uses of this library, I have neither missed float, decimal, double double or BigDecimal. I believe that goes for the majority of users too.

I can see that for astronomical units and very big/small units, a greater range and precision may be required. I'm speculating that this is may be a minority use case though.

I'm happy to support it, but only if it doesn't complicate the simple, mainstream use cases or the learning curve and discoverability. Either by adding new complicated concepts to understand, multiple types or generics to choose from or maybe placing types in different namespaces.

I'm simply trying to keep it simple and make sure we only create something that people actually will use and benefit from.

One thing that would help immensely for me at least, is to have a summary of

  • What we are trying to solve (say, astronomical units)
  • Who will use this and what for? Some specific persons to discuss scenarios with.
  • A concise design outline of what are we adding or changing, any breaking changes etc. Something to discuss around.

Cheers 🍻

@ebfortin
Copy link
Contributor

ebfortin commented Jun 7, 2020

You are right. Before going forward with such an extensive change we should make sure it has benefits accross the board. For me it just make sense to have that possibility. But there should be hard facts attached to it.

# Conflicts:
#	CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
#	CodeGen/Generators/UnitsNetGen/StaticQuantityGenerator.cs
#	CodeGen/Generators/UnitsNetGen/UnitTestBaseClassGenerator.cs
#	UnitsNet.Tests/CompiledLambdasTests.cs
#	UnitsNet.Tests/CustomCode/AmountOfSubstanceTests.cs
#	UnitsNet.Tests/CustomCode/LengthTests.FeetInches.cs
#	UnitsNet.Tests/CustomCode/MassConcentrationTests.cs
#	UnitsNet.Tests/CustomCode/PressureTests.cs
#	UnitsNet.Tests/GeneratedCode/AmountOfSubstanceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/AmplitudeRatioTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ApparentEnergyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ApparentPowerTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/AreaDensityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/AreaMomentOfInertiaTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/BrakeSpecificFuelConsumptionTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/CapacitanceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/CoefficientOfThermalExpansionTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/DurationTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/DynamicViscosityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricAdmittanceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricChargeDensityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricChargeTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricConductanceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricConductivityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricCurrentDensityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricCurrentGradientTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricCurrentTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricFieldTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricInductanceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricPotentialAcTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricPotentialDcTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricPotentialTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricResistanceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ElectricSurfaceChargeDensityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/EnergyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/EntropyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ForcePerLengthTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ForceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/FrequencyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/FuelEfficiencyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/HeatTransferCoefficientTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/IQuantityTests.g.cs
#	UnitsNet.Tests/GeneratedCode/IlluminanceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/KinematicViscosityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/LapseRateTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/LevelTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/LinearDensityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/LuminosityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/LuminousFluxTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/LuminousIntensityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/MagneticFieldTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/MagneticFluxTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/MagnetizationTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/MassFluxTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/MolarEnergyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/MolarEntropyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/MolarityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/PermeabilityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/PermittivityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/PowerRatioTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/PowerTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/PressureChangeRateTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/RatioChangeRateTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/RatioTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ReactiveEnergyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ReactivePowerTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/RotationalAccelerationTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/RotationalStiffnessPerLengthTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/RotationalStiffnessTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/SolidAngleTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/SpecificEnergyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/SpecificVolumeTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TemperatureDeltaTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TemperatureTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/AccelerationTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/AngleTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/AreaTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/BitRateTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/DensityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/ElectricResistivityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/ForceChangeRateTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/HeatFluxTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/InformationTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/IrradianceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/IrradiationTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/LengthTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/MassConcentrationTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/MassFlowTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/MassFractionTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/MassMomentOfInertiaTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/MassTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/MolarMassTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/PowerDensityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/PressureTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/RotationalSpeedTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/SpecificEntropyTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/SpecificWeightTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/SpeedTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/TemperatureChangeRateTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/TorqueTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/VolumeConcentrationTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/VolumeFlowTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/TestsBase/VolumeTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ThermalConductivityTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/ThermalResistanceTestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/VitaminATestsBase.g.cs
#	UnitsNet.Tests/GeneratedCode/VolumePerLengthTestsBase.g.cs
#	UnitsNet.Tests/QuantityInfoTest.cs
#	UnitsNet.Tests/QuantityTests.Ctor.cs
#	UnitsNet.Tests/QuantityTests.ToString.cs
#	UnitsNet.Tests/QuantityTests.cs
#	UnitsNet/CompiledLambdas.cs
#	UnitsNet/CustomCode/Quantities/ForcePerLength.extra.cs
#	UnitsNet/CustomCode/Quantities/Length.extra.cs
#	UnitsNet/CustomCode/Quantity.cs
#	UnitsNet/GeneratedCode/Quantities/Acceleration.g.cs
#	UnitsNet/GeneratedCode/Quantities/AmountOfSubstance.g.cs
#	UnitsNet/GeneratedCode/Quantities/AmplitudeRatio.g.cs
#	UnitsNet/GeneratedCode/Quantities/Angle.g.cs
#	UnitsNet/GeneratedCode/Quantities/ApparentEnergy.g.cs
#	UnitsNet/GeneratedCode/Quantities/ApparentPower.g.cs
#	UnitsNet/GeneratedCode/Quantities/Area.g.cs
#	UnitsNet/GeneratedCode/Quantities/AreaDensity.g.cs
#	UnitsNet/GeneratedCode/Quantities/AreaMomentOfInertia.g.cs
#	UnitsNet/GeneratedCode/Quantities/BitRate.g.cs
#	UnitsNet/GeneratedCode/Quantities/BrakeSpecificFuelConsumption.g.cs
#	UnitsNet/GeneratedCode/Quantities/Capacitance.g.cs
#	UnitsNet/GeneratedCode/Quantities/CoefficientOfThermalExpansion.g.cs
#	UnitsNet/GeneratedCode/Quantities/Density.g.cs
#	UnitsNet/GeneratedCode/Quantities/Duration.g.cs
#	UnitsNet/GeneratedCode/Quantities/DynamicViscosity.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricAdmittance.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricCharge.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricChargeDensity.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricConductance.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricConductivity.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricCurrent.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricCurrentDensity.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricCurrentGradient.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricField.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricInductance.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricPotential.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricPotentialAc.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricPotentialDc.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricResistance.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricResistivity.g.cs
#	UnitsNet/GeneratedCode/Quantities/ElectricSurfaceChargeDensity.g.cs
#	UnitsNet/GeneratedCode/Quantities/Energy.g.cs
#	UnitsNet/GeneratedCode/Quantities/Entropy.g.cs
#	UnitsNet/GeneratedCode/Quantities/Force.g.cs
#	UnitsNet/GeneratedCode/Quantities/ForceChangeRate.g.cs
#	UnitsNet/GeneratedCode/Quantities/ForcePerLength.g.cs
#	UnitsNet/GeneratedCode/Quantities/Frequency.g.cs
#	UnitsNet/GeneratedCode/Quantities/FuelEfficiency.g.cs
#	UnitsNet/GeneratedCode/Quantities/HeatFlux.g.cs
#	UnitsNet/GeneratedCode/Quantities/HeatTransferCoefficient.g.cs
#	UnitsNet/GeneratedCode/Quantities/Illuminance.g.cs
#	UnitsNet/GeneratedCode/Quantities/Information.g.cs
#	UnitsNet/GeneratedCode/Quantities/Irradiance.g.cs
#	UnitsNet/GeneratedCode/Quantities/Irradiation.g.cs
#	UnitsNet/GeneratedCode/Quantities/KinematicViscosity.g.cs
#	UnitsNet/GeneratedCode/Quantities/LapseRate.g.cs
#	UnitsNet/GeneratedCode/Quantities/Length.g.cs
#	UnitsNet/GeneratedCode/Quantities/Level.g.cs
#	UnitsNet/GeneratedCode/Quantities/LinearDensity.g.cs
#	UnitsNet/GeneratedCode/Quantities/Luminosity.g.cs
#	UnitsNet/GeneratedCode/Quantities/LuminousFlux.g.cs
#	UnitsNet/GeneratedCode/Quantities/LuminousIntensity.g.cs
#	UnitsNet/GeneratedCode/Quantities/MagneticField.g.cs
#	UnitsNet/GeneratedCode/Quantities/MagneticFlux.g.cs
#	UnitsNet/GeneratedCode/Quantities/Magnetization.g.cs
#	UnitsNet/GeneratedCode/Quantities/Mass.g.cs
#	UnitsNet/GeneratedCode/Quantities/MassConcentration.g.cs
#	UnitsNet/GeneratedCode/Quantities/MassFlow.g.cs
#	UnitsNet/GeneratedCode/Quantities/MassFlux.g.cs
#	UnitsNet/GeneratedCode/Quantities/MassFraction.g.cs
#	UnitsNet/GeneratedCode/Quantities/MassMomentOfInertia.g.cs
#	UnitsNet/GeneratedCode/Quantities/MolarEnergy.g.cs
#	UnitsNet/GeneratedCode/Quantities/MolarEntropy.g.cs
#	UnitsNet/GeneratedCode/Quantities/MolarMass.g.cs
#	UnitsNet/GeneratedCode/Quantities/Molarity.g.cs
#	UnitsNet/GeneratedCode/Quantities/Permeability.g.cs
#	UnitsNet/GeneratedCode/Quantities/Permittivity.g.cs
#	UnitsNet/GeneratedCode/Quantities/Power.g.cs
#	UnitsNet/GeneratedCode/Quantities/PowerDensity.g.cs
#	UnitsNet/GeneratedCode/Quantities/PowerRatio.g.cs
#	UnitsNet/GeneratedCode/Quantities/Pressure.g.cs
#	UnitsNet/GeneratedCode/Quantities/PressureChangeRate.g.cs
#	UnitsNet/GeneratedCode/Quantities/Ratio.g.cs
#	UnitsNet/GeneratedCode/Quantities/RatioChangeRate.g.cs
#	UnitsNet/GeneratedCode/Quantities/ReactiveEnergy.g.cs
#	UnitsNet/GeneratedCode/Quantities/ReactivePower.g.cs
#	UnitsNet/GeneratedCode/Quantities/RotationalAcceleration.g.cs
#	UnitsNet/GeneratedCode/Quantities/RotationalSpeed.g.cs
#	UnitsNet/GeneratedCode/Quantities/RotationalStiffness.g.cs
#	UnitsNet/GeneratedCode/Quantities/RotationalStiffnessPerLength.g.cs
#	UnitsNet/GeneratedCode/Quantities/SolidAngle.g.cs
#	UnitsNet/GeneratedCode/Quantities/SpecificEnergy.g.cs
#	UnitsNet/GeneratedCode/Quantities/SpecificEntropy.g.cs
#	UnitsNet/GeneratedCode/Quantities/SpecificVolume.g.cs
#	UnitsNet/GeneratedCode/Quantities/SpecificWeight.g.cs
#	UnitsNet/GeneratedCode/Quantities/Speed.g.cs
#	UnitsNet/GeneratedCode/Quantities/Temperature.g.cs
#	UnitsNet/GeneratedCode/Quantities/TemperatureChangeRate.g.cs
#	UnitsNet/GeneratedCode/Quantities/TemperatureDelta.g.cs
#	UnitsNet/GeneratedCode/Quantities/ThermalConductivity.g.cs
#	UnitsNet/GeneratedCode/Quantities/ThermalResistance.g.cs
#	UnitsNet/GeneratedCode/Quantities/Torque.g.cs
#	UnitsNet/GeneratedCode/Quantities/VitaminA.g.cs
#	UnitsNet/GeneratedCode/Quantities/Volume.g.cs
#	UnitsNet/GeneratedCode/Quantities/VolumeConcentration.g.cs
#	UnitsNet/GeneratedCode/Quantities/VolumeFlow.g.cs
#	UnitsNet/GeneratedCode/Quantities/VolumePerLength.g.cs
#	UnitsNet/GeneratedCode/Quantity.g.cs
#	UnitsNet/GeneratedCode/UnitConverter.g.cs
#	UnitsNet/QuantityTypeConverter.cs
#	UnitsNet/UnitConverter.cs
- Add `struct` generic constraint to T
- Add GenericNumberHelper for MinValue, MaxValue
- Add T to Parse
- Add T to Equals

The biggest blocker is the conversion functions, how to take dynamic
code from JSON and make it work with generics.

UnitsNet.Angle.GetValueAs
UnitsNet.Angle.GetValueInBaseUnit
@angularsen
Copy link
Owner

@tmilnthorp I took a look, merged in latest master and tried to fix a bunch of compile errors.

  • Add struct generic constraint to T
  • Add GenericNumberHelper for MinValue, MaxValue
  • Add T to Parse
  • Add T to Equals

The biggest blocker I'm seeing right now are the conversion functions.
How do we take dynamic C# code from JSON text and make it work with generics? Sounds tricky to me.

UnitsNet.Angle.GetValueAs
UnitsNet.Angle.GetValueInBaseUnit

Non-trivial example from #883

"FromBaseToUnitFunc": "UnitsNetMath.Clamp(System.Math.Round(x / 0.125, MidpointRounding.AwayFromZero),0,8)",

@ebfortin
Copy link
Contributor

Now that Net 7 preview 5 is 9ut generic math are a lot more advanced in their development. There exist an IFloatingPoint interface that constraint numbers to floating point types and this includes decimal. It could be used to make sure no integer types are used.

I think using generics as the core of UnitsNet makes a lot of sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pinned Issues that should not be auto-closed due to inactivity.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants