具有泛型类型约束的泛型方法的笨拙语法

时间:2015-03-01 20:19:46

标签: c# generics units-of-measurement

我正在尝试创建一个测量类库。

到目前为止我所拥有的是:

public abstract class UnitBase
{
    protected double unitValue;
    protected readonly double toSiFactor;
    //more stuff goes here
}

//Meters, feet, miles etc will derive from this
public class LengthUnit : UnitBase
{
    //various methods and operator overloading

    //Among others there is To<> for conversions, not really needed
    //to understand the problem but relevant.
    public L To<L>() where L : LengthUnit, new()
    {
        L converted = new L();
        converted.FromSi(ToSi());  //ToSi() and FromSi() are in UnitBase, omitted for brevity
        return converted;
    }
}

//Seconds Hours etc will derive from this
public class TimeUnit : UnitBase
{
    //Add() Subtract methods and various operator overloading
}

到目前为止一切顺利。但现在我想创建复杂的单位类型,如速度。所以这就是:

public class SpeedUnit<S, T> : UnitBase
    where S : LengthUnit, new()
    where T : TimeUnit, new()
{
    //=======================
    //HERE IS THE AWKWARDNESS
    //=======================
    public U To<U, S1, T1>()
        where U : SpeedUnit<S1, T1>, new()
        where S1 : LengthUnit, new()
        where T1 : TimeUnit, new()
    {
        U converted = new U();
        converted.FromSi(ToSi());
        return converted;
    }
}

public class Knots : SpeedUnit<NauticalMiles, Hours>
{
    //omitted code
}

public class FeetPerMinute : SpeedUnit<Feet, Minutes>
{
    //omitted code
}

所以这是我的问题:假设你有结,并且你想将它们转换为FeetPerMinute。理想的是:

Knots kts = new Knots(20);
FeetPerMinute = kts.To<FeetPerMinute>();

相反,我必须这样做:

FeetPerMinute = kts.To<FeetPerMinute, Feet, Minutes>();

这有点尴尬,当涉及更复杂的类型,如力量时,可能会使事件变得更糟。 To()将类似于:

Newtons n = someForce.To<Newtons, Kilograms, Meters, Seconds>()

如果你错误地使用加速类型,那就更糟了:

Newtons n = someForce.To<Newtons, Kilograms, Acceleration<Meters, Seconds>, Meters, Seconds>()

不是很方便,特别是如果你有简单的想法。 所以我的问题是:

  • 有没有办法让这项工作? (除了从SpeedUnit删除通用参数)
  • 为什么编译器的臭名昭着的类型推断无法发现Meters已经存在SecondsMetersPerSecond

1 个答案:

答案 0 :(得分:2)

简短的回答是C#不允许从其他泛型类型推断泛型类型,仅允许从参数中推断。你也可以说泛型类型限制对此没有足够的压力。

但是,我真的不明白为什么你甚至将不同的单位代表不同的类别?我个人的建议是对每个物理维度使用结构 - 而不是单位。所以,有这样的结构:

public struct Length {
    public static Length FromMeters(double meters) {
        // ...
    }

    public double InMiles() { ... }

    // operator overloads
}

这样您就不会遇到泛型问题,甚至不会产生运行时影响,但仍然有编译时支持。

假设您有LengthTimeSpeed结构,那么您可以轻松地重载允许您将Length除以Time的运算符获得Speed作为结果。然后,您可以通过一种方法(速度)以任意单位查询速度值,例如: InMilesPerSecond返回双倍。

修改 我想你原来的想法中的真正问题是错误的抽象问题。特别是,速度不是(在身份的意义上)长度除以时间,尽管你可以这样表达。这是精细和微妙的区别。例如,您可以表示1W = 1Nm或1W = 1VA。因此,您不应该为PowerType,Length&gt;建模。因为这不是权力的同一性,而只是计算它的一种方式。