从非泛型LineSegmentClass继承的Generic Vector类

时间:2014-08-07 14:28:15

标签: c# generics vector

我正在尝试编写一个可以表示Forces和Dimensions的Vector类。通常这不是问题,因为大多数人都会创建一个名为Magnitude的属性,它返回一个double。 Double不关心他们代表什么,他们代表力量或尺寸就好了。但是,我使用this open-source library called Unit Class Library来表示维度和力量。

这是我想做的事情。有没有办法让属性返回一个不同的类型,具体取决于T(在构造函数中会发生同样的事情)。是否有可能,如果是这样,怎么样?

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

        ...
    }

或者,使用dynamic关键字可以做些什么来实现这个目标?

或者我应该将这样的一组接口放在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; }
}

然后创建一个Hybrid类,实现两者作为Class的Magnitude。

我运行Unit Class Library因此欢迎提出应该进行的任何更改。但请记住,库的直观性和单位转换能力是我想要保留的项目的价值。

4 个答案:

答案 0 :(得分:2)

你绝对不想做那种类型检查 - 你最好做的是创建一个抽象基类或接口,然后从中派生。

例如,如果您使用抽象类:

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

答案 1 :(得分:2)

让我提供和替代单位库。这将使您不必处理泛型,并可以使用派生单位。以下是第一个例子:

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

    }
}

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

将在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 :(得分:2)

为什么不从Vector派生并丢弃作为Dimension的父Magnitude值?

有这样的东西吗?

public class Load:Vector
{

    private ForceUnit _magnitude;

    public ForceUnit Magnitude
    {
        get
        {
                return this._magnitude;          
        }
    }



    public PointLoad(ForceUnit magnitudeOfLoad, Vector directionVector)
        : base(...)
    {
        _magnitude = magnitudeOfLoad;
    }

}

答案 3 :(得分:1)

您可以返回一个对象或动态,并将其强制转换。

但是,我建议你不要这样做,即使没有意义。 我建议你检查一下你的逻辑。

public object Magnitude
    {
        get {

            Type typeT = typeof(T);

            if (typeT == typeof(Dimension))
            {
                return base.Length;
            }
            else if (typeT == typeof(ForceUnit))
            {
                return _magnitudeForce;
            }

        ...
    }