Generic Vector class inheriting from non-generic LineSegmentClass class
I am trying to write a Vector class that can represent Forces and Dimensions. This is usually not a problem because most people create a Magnitude property that returns a double. The doubles don't care what they represent, they represent forces or dimensions just fine. However, I use this open source library called the class class library to represent dimensions and forces.
Here's what I would like to do. Is there a way to return a property to a different type depending on T
(The same will happen in the constructor). Is it possible, and if so, how?
public class Vector<T> : LineSegment
{
ForceUnit _magnitudeForce;
/// <summary>
/// Returns the magnitude of the vector (equals the length of the line segment parent if T is Dimension, if T is Force, return variable )
/// </summary>
public T Magnitude
{
get {
Type typeT = typeof(T);
if (typeT == typeof(Dimension))
{
return base.Length;
}
else if (typeT == typeof(ForceUnit))
{
return _magnitudeForce;
}
...
}
Or is there something tricky that I can do with the keyword dynamic
to achieve this?
Or should I make a bunch of interfaces like this to navigate to the Unit Library :
interface IDistance
{
double Centimeters { get; }
double Feet { get; }
double Inches { get; }
double Kilometers { get; }
double Meters { get; }
double Miles { get; }
double Millimeters { get; }
double Sixteenths { get; }
double ThirtySeconds { get; }
double Yards { get; }
}
interface IForceUnit
{
double Kips { get; }
double Newtons { get; }
double Pounds { get; }
}
And then create a hybrid class that implements both values to be used as Mass for the class.
I ran the Device Class Library , so any changes that need to be made there will be suggested. But keep in mind that the ability to intuition and convert library units is the value of the project I want to keep.
source to share
Why not just deduce from the vector and throw away the parent Magnitude, which is the size?
With something like this?
public class Load:Vector
{
private ForceUnit _magnitude;
public ForceUnit Magnitude
{
get
{
return this._magnitude;
}
}
public PointLoad(ForceUnit magnitudeOfLoad, Vector directionVector)
: base(...)
{
_magnitude = magnitudeOfLoad;
}
}
source to share
You definitely don't want to do this type checking - what you better do is create an abstract base class or interface and then extract from it.
For example, if you are using an abstract class:
public abstract class Vector<T> : LineSegment
{
public abstract T Magnitude { get; }
}
public class DimensionVector : Vector<Dimension>
{
public override Dimension Magnitude { get { return base.Length; } }
}
public class ForceUnitVector : Vector<ForceUnit>
{
ForceUnit magnitudeForce;
public override ForceVector Magnitude { get { return this.magnitudeForce; } }
}
source to share
Let me suggest an alternative library of units. This will keep you from dealing with generics and can work with derived units. Here's an example:
class Program
{
static void Main(string[] args)
{
Vector A=new Vector(Unit.Foot, 0.3, 0.5, 0.7, 1.0);
Vector B=A.ConvertTo(Unit.Inch);
Vector C=B*B; // Convert to square inches, compatible with SI units of m^2
Debug.WriteLine(A.ToString()); // [0.3,0.5,0.7,1]
Debug.WriteLine(B.ToString()); // [3.6,6,8.4,12]
Debug.WriteLine(C.ToString()); // [12.96,36,70.56,144]
Vector F=new Vector(Unit.PoundForce, 100.0, 130.0, 150.0, 180.0);
Vector K=F/B; // Stiffness is force per distance, compatible with SI units of kg/m^2
Vector P=F/C; // Pressure is force per area, compatible with SI units kg/(m*s^2)
Debug.WriteLine(F.ToString()); // [100,130,150,180]
Debug.WriteLine(K.ToString()); // [27.78,21.67,17.86,15]
Debug.WriteLine(P.ToString()); // [7.716,3.611,2.126,1.25]
var x=3*Unit.Foot.FactorTo(Unit.Inch); // x=36 inches
}
}
Class Unit
:
public class Unit : IEquatable<Unit>
{
readonly int M, L, T; // base unit powers. For example Area: (M=0, L=2, T=0)
readonly double x; // base unit factor. For example 1 km = (1000)*m
public Unit(int M, int L, int T, double factor)
{
this.M=M;
this.L=L;
this.T=T;
this.x=factor;
}
public Unit(Unit other)
{
this.M=other.M;
this.L=other.L;
this.T=other.T;
this.x=other.x;
}
public int[] Dimension { get { return new int[] { M, L, T }; } }
public double Factor { get { return x; } }
public bool IsConvertibleTo(Unit other) { return M==other.M&&L==other.L&&T==other.T; }
public double FactorTo(Unit target)
{
if(IsConvertibleTo(target))
{
return Factor/target.Factor;
}
throw new ArgumentException("Incompatible units in target.");
}
#region IEquatable Members
/// <summary>
/// Equality overrides from <see cref="System.Object"/>
/// </summary>
/// <param name="obj">The object to compare this with</param>
/// <returns>False if object is a different type, otherwise it calls <code>Equals(Unit)</code></returns>
public override bool Equals(object obj)
{
if(obj is Unit)
{
return Equals((Unit)obj);
}
return false;
}
/// <summary>
/// Checks for equality among <see cref="Unit"/> classes
/// </summary>
/// <param name="other">The other <see cref="Unit"/> to compare it to</param>
/// <returns>True if equal</returns>
public bool Equals(Unit other)
{
return M==other.M
&&L==other.L
&&T==other.T
&&x.Equals(other.x);
}
/// <summary>
/// Calculates the hash code for the <see cref="Unit"/>
/// </summary>
/// <returns>The int hash value</returns>
public override int GetHashCode()
{
return (((17*23+M.GetHashCode())*23+L.GetHashCode())*23+T.GetHashCode())*23+x.GetHashCode();
}
#endregion
public override string ToString()
{
return string.Format("{0}(M:{1},L:{2},T:{3})", Factor, M, L, T);
}
public static Unit operator*(double relative, Unit unit)
{
return new Unit(unit.M, unit.L, unit.T, relative*unit.Factor);
}
public static Unit operator/(Unit unit, double divisor)
{
return new Unit(unit.M, unit.L, unit.T, unit.Factor/divisor);
}
public static Unit operator*(Unit unit, Unit other)
{
return new Unit(
unit.M+other.M,
unit.L+other.L,
unit.T+other.T,
unit.Factor*other.Factor);
}
public static Unit operator/(Unit unit, Unit other)
{
return new Unit(
unit.M-other.M,
unit.L-other.L,
unit.T-other.T,
unit.Factor/other.Factor);
}
public static Unit operator^(Unit unit, int power)
{
return new Unit(
unit.M*power,
unit.L*power,
unit.T*power,
Math.Pow(unit.Factor, power));
}
public static readonly Unit Meter=new Unit(0, 1, 0, 1.0);
public static readonly Unit Millimeter=0.001*Meter;
public static readonly Unit Inch=25.4*Millimeter;
public static readonly Unit Foot=12*Inch;
public static readonly Unit Yard=3*Foot;
public static readonly Unit Second=new Unit(0, 0, 1, 1.0);
public static readonly Unit Minute=60*Second;
public static readonly Unit Hour=60*Minute;
public static readonly Unit Kilogram=new Unit(1, 0, 0, 1.0);
public static readonly Unit PoundMass=0.453592*Kilogram;
public static readonly Unit Newton=Kilogram*(Meter/(Second^2));
public static readonly Unit PoundForce=4.44822162*Newton;
}
which will be used in the class Vector
public class Vector
{
public Vector(Unit unit, int size)
{
this.Elements=new double[size];
this.Unit=unit;
}
public Vector(Unit unit, params double[] values)
{
this.Unit=unit;
this.Elements=values;
}
public Unit Unit { get; private set; }
public double[] Elements { get; private set; }
public double this[int index] { get { return Elements[index]; } }
public int Count { get { return Elements.Length; } }
public Vector ConvertTo(Unit target)
{
double factor=Unit.FactorTo(target);
double[] values=Array.ConvertAll(Elements, (x) => factor*x);
return new Vector(target, values);
}
public override string ToString()
{
string[] items=Array.ConvertAll(Elements, (x) => x.ToString());
return string.Format("[{0}] in {1}", string.Join(",", items), Unit.ToString());
}
public static Vector operator+(Vector x, Vector y)
{
if(x.Count==y.Count)
{
x=x.ConvertTo(y.Unit);
double[] result=new double[x.Count];
for(int i=0; i<result.Length; i++)
{
result[i]=x[i]+y[i];
}
return new Vector(y.Unit, result);
}
throw new IndexOutOfRangeException("Vectors must have the same number of elements.");
}
public static Vector operator-(Vector x, Vector y)
{
if(x.Count==y.Count)
{
x=x.ConvertTo(y.Unit);
double[] result=new double[x.Count];
for(int i=0; i<result.Length; i++)
{
result[i]=x[i]-y[i];
}
return new Vector(y.Unit, result);
}
throw new IndexOutOfRangeException("Vectors must have the same number of elements.");
}
public static Vector operator*(double x, Vector y)
{
double[] result=new double[y.Count];
for(int i=0; i<result.Length; i++)
{
result[i]=x*y[i];
}
return new Vector(y.Unit, result);
}
public static Vector operator*(Vector x, Vector y)
{
if(x.Count==y.Count)
{
double[] result=new double[x.Count];
for(int i=0; i<result.Length; i++)
{
result[i]=x[i]*y[i];
}
return new Vector(x.Unit* y.Unit, result);
}
throw new IndexOutOfRangeException("Vectors must have the same number of elements.");
}
public static Vector operator/(Vector x, Vector y)
{
if(x.Count==y.Count)
{
double[] result=new double[x.Count];
for(int i=0; i<result.Length; i++)
{
result[i]=x[i]/y[i];
}
return new Vector(x.Unit/y.Unit, result);
}
throw new IndexOutOfRangeException("Vectors must have the same number of elements.");
}
}
source to share
You can return an object or dynamics and throw it.
However, I advise you not to do this, even because it doesn't make sense. I advise you to reconsider your logic.
public object Magnitude
{
get {
Type typeT = typeof(T);
if (typeT == typeof(Dimension))
{
return base.Length;
}
else if (typeT == typeof(ForceUnit))
{
return _magnitudeForce;
}
...
}
source to share