patterncsharpMinor
Encapsulated double for type safety
Viewed 0 times
typedoublesafetyforencapsulated
Problem
Link to a full functional solution.
I'm working on a physics based algorithm, and I find myself working a lot with functions of style
A lot of physical values are passed from function to function as double, and it is a nightmare. I fail a lot at giving the right value at the right parameter, and also at the good units. Sometimes speed is in m/s, sometimes in km/h.
So my idea was to create an abstract class for different physical values, like Speed, Angle, etc...
Since you can add doubles, you can also add speeds or angles, so the abstract class must support adding, soustracting, all basic operations you can do on double.
So here is my abstract class :
```
public interface ISIQuantity
{
double SIValue { get; }
}
///
/// Represent maths or physics quantities like angle, mass, speed
/// http://en.wikipedia.org/wiki/International_System_of_Units
///
///
public abstract class SIQuantity : ISIQuantity, IComparable, IComparable, IEquatable
where T : ISIQuantity
{
private readonly double _value;
public double SIValue { get { return _value; } }
public SIQuantity(double value)
{
this._value = value;
}
public int CompareTo(T other) { return this.SIValue.CompareTo(other.SIValue); }
public int CompareTo(object other)
{
if (other is T)
{
return this.CompareTo(((T)other));
}
else
{
return 1;
}
}
public bool Equals(T other) { return this.SIValue.Equals(other.SIValue); }
public override bool Equals(object other)
{
if (other is T)
{
return this.Equals(((T)other));
}
else
{
return false;
}
}
public override int GetHashCode()
{
return this._value.GetHashCode();
}
public static bool operator ==(SIQuantity a, SIQuantity b)
{
// If both are null, or bot
I'm working on a physics based algorithm, and I find myself working a lot with functions of style
double GetSpeed(double acceleration, double angle, double resistance)A lot of physical values are passed from function to function as double, and it is a nightmare. I fail a lot at giving the right value at the right parameter, and also at the good units. Sometimes speed is in m/s, sometimes in km/h.
So my idea was to create an abstract class for different physical values, like Speed, Angle, etc...
Since you can add doubles, you can also add speeds or angles, so the abstract class must support adding, soustracting, all basic operations you can do on double.
So here is my abstract class :
```
public interface ISIQuantity
{
double SIValue { get; }
}
///
/// Represent maths or physics quantities like angle, mass, speed
/// http://en.wikipedia.org/wiki/International_System_of_Units
///
///
public abstract class SIQuantity : ISIQuantity, IComparable, IComparable, IEquatable
where T : ISIQuantity
{
private readonly double _value;
public double SIValue { get { return _value; } }
public SIQuantity(double value)
{
this._value = value;
}
public int CompareTo(T other) { return this.SIValue.CompareTo(other.SIValue); }
public int CompareTo(object other)
{
if (other is T)
{
return this.CompareTo(((T)other));
}
else
{
return 1;
}
}
public bool Equals(T other) { return this.SIValue.Equals(other.SIValue); }
public override bool Equals(object other)
{
if (other is T)
{
return this.Equals(((T)other));
}
else
{
return false;
}
}
public override int GetHashCode()
{
return this._value.GetHashCode();
}
public static bool operator ==(SIQuantity a, SIQuantity b)
{
// If both are null, or bot
Solution
The main issue in your code is that you don't control proper combination of units in operations, e.g. you allow summing up speeds with kilograms.
I would rather create a class (or maybe better a struct?) that is aware of unit types as well, and create a number of static/extension methods/operator overloads to convert the value from different units...
Example out of my head (not tested since don't have VS at hand):
I would rather create a class (or maybe better a struct?) that is aware of unit types as well, and create a number of static/extension methods/operator overloads to convert the value from different units...
Example out of my head (not tested since don't have VS at hand):
public struct Unit
{
public sbyte Meters {get; private set}
public sbyte Kilograms {get; private set}
public sbyte Seconds {get; private set}
public sbyte Amperes {get; private set}
public sbyte Kelvins {get; private set}
public sbyte Candelas {get; private set}
public sbyte Moles {get; private set}
public Unit(sbyte meters = 0, sbyte kilograms = 0, sbyte seconds = 0,
sbyte amperes = 0, sbyte kelvins = 0, sbyte candelas = 0, sbyte moles = 0)
{
Meters = meters;
Kilograms = kilograms;
Seconds = seconds;
Amperes = amperes;
Kelvins = kelvins;
Candelas = candelas;
Moles = moles;
}
//TODO: add operators that combine different units by adding corresponding units. It can be implemented via sbyte[] but I find it more readable if implemented as separate properties. Since it has to be implemented once in this struct it should not be a big deal.
//TODO: you can override ToString and output readable derived SI units like hertz/newton/joule/pascal instead of combination of base units...
//TODO: Add static methods to define common base/derived units, e.g.:
public static Unit Speed()
{
return new Unit(meters = 1, seconds = -1);
}
public static Unit Newton()
{
return new Unit(kilograms = 1, meters = 1, seconds = -2);
}
}
public struct UnitValue
{
public double Value {get; private set}
public Unit Unit {get; private set}
public UnitValue(double value, Unit unit)
{
Value = value;
Unit = unit;
}
//TODO: overload operators so that they allow summing up unit values with equal units only, and "sum" units in case of multiplication.
//TODO: add static methods to define unit values from non-standard units, e.g.:
public static UnitValue FromKilometersPerHour(double value)
{
return new UnitValue(value / 3.6, Unit.Speed());
}
}Code Snippets
public struct Unit
{
public sbyte Meters {get; private set}
public sbyte Kilograms {get; private set}
public sbyte Seconds {get; private set}
public sbyte Amperes {get; private set}
public sbyte Kelvins {get; private set}
public sbyte Candelas {get; private set}
public sbyte Moles {get; private set}
public Unit(sbyte meters = 0, sbyte kilograms = 0, sbyte seconds = 0,
sbyte amperes = 0, sbyte kelvins = 0, sbyte candelas = 0, sbyte moles = 0)
{
Meters = meters;
Kilograms = kilograms;
Seconds = seconds;
Amperes = amperes;
Kelvins = kelvins;
Candelas = candelas;
Moles = moles;
}
//TODO: add operators that combine different units by adding corresponding units. It can be implemented via sbyte[] but I find it more readable if implemented as separate properties. Since it has to be implemented once in this struct it should not be a big deal.
//TODO: you can override ToString and output readable derived SI units like hertz/newton/joule/pascal instead of combination of base units...
//TODO: Add static methods to define common base/derived units, e.g.:
public static Unit Speed()
{
return new Unit(meters = 1, seconds = -1);
}
public static Unit Newton()
{
return new Unit(kilograms = 1, meters = 1, seconds = -2);
}
}
public struct UnitValue
{
public double Value {get; private set}
public Unit Unit {get; private set}
public UnitValue(double value, Unit unit)
{
Value = value;
Unit = unit;
}
//TODO: overload operators so that they allow summing up unit values with equal units only, and "sum" units in case of multiplication.
//TODO: add static methods to define unit values from non-standard units, e.g.:
public static UnitValue FromKilometersPerHour(double value)
{
return new UnitValue(value / 3.6, Unit.Speed());
}
}Context
StackExchange Code Review Q#24623, answer score: 2
Revisions (0)
No revisions yet.