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.

+3


source to share


4 answers


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;
    }

}

      

+2


source


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; } }
}

      

+2


source


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.");
    }
}

      

+2


source


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;
            }

        ...
    }

      

+1


source







All Articles