10

I have a project with many calculations involving a lot of real world units :

  • Distance;
  • Temperature;
  • Flow rate;
  • ...

This project involves complicated and numerous calculation formulas.

That's why I supposed the use of custom types like Temperature, Distance... can be good for code readability. For example:

Temperature x = -55.3;
Meter y = 3;

or

var x = new Temperature(-55.3);

I tried to make a Temperature class that uses a double internal value.

public class Temperature
{
    double _Value = double.NaN;

    public Temperature() { }

    public Temperature(double v) {
        _Value = v;
    }

    public static implicit operator Temperature(double v) {
        return new Temperature(v);
    }
}

But class are nullable. This mean that something like :

Temperature myTemp;

is "correct" and will be null. I dont want this. I dont want to use structs because they are too limited :

  • They cannot use parameterless constructor nor instance field intializers like double _Value = double.Nan; to define a default value (I wand default underlying double value to be NaN)
  • They cannot inherits from classes, they only can implement Interfaces

Them I wonder whether there is a way to tell C#:

Temperature myTemp = 23K; // C# does not implement anything to make K unit...

but I know C# does not handle no custom units.

Temperature myTemp = new Kelvin(23); // This might work

So I imagine I could create two Celsius and Kelvin classes that inherits from Temperature, and then I started to wonder if the idea really worth it, because it involves a lot of coding and testing.

That's the discussion I would like to start :

Would the use of real world units in my code instead of .NET types would be a good thing or not ? Did anyone this already ? What are the pitfalls and the best practices ? Or should I better keep stay away from this and use standard .NET types ?

Larry
  • 17,605
  • 9
  • 77
  • 106
  • 2
    Sidenote: F# supports units natively – CodesInChaos Oct 22 '10 at 10:28
  • 7
    Why do you think structs are limited? I think structs are exactly what I'll use here. Your classes will carry one numeric, immutable value only - ideal use case for structs. – NOtherDev Oct 22 '10 at 10:29
  • Interresting! Unfortunately I cannot afford to use F# in my project. But I definitely note this for the future – Larry Oct 22 '10 at 10:30
  • Structs force me to override each operators, and they cannot derive from classes. They can only implement interfaces. I affraid it could make things ennoying later. – Larry Oct 22 '10 at 10:31
  • 1
    * What do you mean "structs force me to override each operator"? What is the limitation specifically? I can't see anything about that in the reference. * Why your Temperature may need to derive from anything? – NOtherDev Oct 22 '10 at 10:34
  • 3
    For what you're using this for, structs would really be a good idea. You're really creating basic data types. Note that e.g. TimeSpan and DateTime are also structs! Also, I would probably not create types like Kelvin and Fahrenheit, but rather just a temperature type in e.g. Kelvin and have conversions (GetCelcius) from that, just how DateTime does this. – Pieter van Ginkel Oct 22 '10 at 10:34
  • @A sorry about operators: the code I tried was wrong... I might reconsider the use of structs since your last input and others. Thanks :) – Larry Oct 22 '10 at 10:44
  • Just falled on this : https://github.com/cureos/csunits – Larry Sep 28 '12 at 05:54
  • Also I recently added http://github.com/InitialForce/UnitsNet . – angularsen Oct 26 '13 at 10:49

6 Answers6

10

Why not try a struct that looks like this:

/// <summary>
/// Temperature class that uses a base unit of Celsius
/// </summary>
public struct Temp
{
    public static Temp FromCelsius(double value)
    {
        return new Temp(value);
    }

    public static Temp FromFahrenheit(double value)
    {
        return new Temp((value - 32) * 5 / 9);
    }

    public static Temp FromKelvin(double value)
    {
        return new Temp(value - 273.15);
    }

    public static Temp operator +(Temp left, Temp right)
    {
        return Temp.FromCelsius(left.Celsius + right.Celsius);
    }

    private double _value;

    private Temp(double value)
    {
        _value = value;
    }

    public double Kelvin
    {
        get { return _value + 273.15; }
    }

    public double Celsius
    {
        get { return _value; }
    }

    public double Fahrenheit
    {
        get { return _value / 5 * 9 + 32; }
    }
}

Then use it like, say, this:

    static void Main(string[] args)
    {
        var c = Temp.FromCelsius(30);
        var f = Temp.FromFahrenheit(20);
        var k = Temp.FromKelvin(20);

        var total = c + f + k;
        Console.WriteLine("Total temp is {0}F", total.Fahrenheit);
    }
OJ.
  • 28,944
  • 5
  • 56
  • 71
  • The problem with this is that you need to gigantic amount of classes and even more overloaded operators. Since in physics *many* combinations of units are useful. – CodesInChaos Oct 22 '10 at 11:50
  • But this is only one class for the idea of temperature. How would you solve it with less given the constraints we have in the language? I don't see it being an issue. Plus overloading will have to happen anyway for each unit. This method minimises them compared to having more classes as suggested in the question. – OJ. Oct 22 '10 at 20:28
  • Beautiful, given our language constraints. For kicks you could even make _value readonly. – Joshua Jan 24 '13 at 16:03
  • Great solution. One thing I'd amend is to avoid repetition - i.e. move your constants to constants rather than having them as typed numbers. – JohnLBevan Aug 30 '13 at 22:48
  • I already do very similar things in Units.NET (http://github.com/InitialForce/UnitsNet) for Length, Area, Volume, Mass, Force, Weight, Pressure and Torque. It is unit-tested and supports PCL. You are welcome to contribute classes for Temperature and other units of measurement that have not yet been added. – angularsen Oct 26 '13 at 10:47
1

One way to achieve this would be to use composition of the basic object (Temperature in your case) with a TemperatureTraits class that specializes the basic object. By analogy to C++, the String equivalent class basic_string is actually a class template (generic in C# terms) that has template parameters not only for the string element (char, wide char) but also a traits class that elaborates on how the class behaves for a given type of string element (e.g. char_traits).

In your case, you might define a generic like

public class MeasurableWithUnits<class M MEASURABLE, class U UNITS>

and then implementation would depend not only on the measurable class but also on the units class. How useful this would be in practice would depend on how much of such an object could be made truly generic - what operations are common across combinations of Measurable and Units?

There is a research paper on C# traits here, if this approach looks interesting.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
  • This is a *really* interresting paper. Unfortunately, I dont think I could apply it for this project because of time constraints, and the complexity of the calculation I have to focus on. +1 anyway and link bookmarked. Thanks ! – Larry Oct 22 '10 at 16:37
  • 1
    @controlbreak - have you seen this related post? http://stackoverflow.com/questions/348853/units-of-measure-in-c-almost – Steve Townsend Oct 22 '10 at 16:50
  • I love that last link. Accepted! – Larry Oct 23 '10 at 18:50
0

I think it can be good when you want to add more specific functionality to a temperature (for example: IsFreezing()).

To solve the issue with Kelvin and Celsius: make an interface ITemperature and a baseclass. In the baseclass, you can implement the interface and fill in the details that are the same for all classes.

Peter van Kekem
  • 1,387
  • 1
  • 12
  • 30
0

If you use a struct, then this cannot be null

struct Temperature 
{ 
    double _Value; 
} 
kyndigs
  • 3,074
  • 1
  • 18
  • 22
0

I don't think it's worth adding static types for units in C#. You would need to overload so many operators(for all unit combinations, not just for all units). And build in functions like Math.Sqrt work on normal doubles,...

What you might try is using dynamic types:

class PhysicalUnit
{
}

struct PhysicalValue
{
    readonly Value;
    readonly PhysicalUnit;
}

And then when compiling in debug mode add checks if the units fit together. And in release just remove the PhysicalUnit field and all the checks, and you're (almost) as fast as code using normal doubles.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
0

I would make Temperature an abstract class that stores the temperature (in Kelvin!) in an InternalTemperature property.

A derived class Celcius would translate the input value internaly to Kelvin. It would have a (readonly) Value property that translated the internal value back.

Comparing them (is one warmer than the other) would then be easy.

Hans Kesting
  • 38,117
  • 9
  • 79
  • 111