我正在尝试创建一个测量单位类库。
我目前拥有的是:
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
}
所以这是我的问题:假设你有 Knots 并且你想将它们转换为 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
和Seconds
已经存在于MetersPerSecond
中?
最佳答案
简短的回答是 C# 不允许从其他泛型类型推断泛型类型,只能从参数中推断。您也可以说泛型类型限制对此的表达力不够。
但是,我真的不明白为什么您甚至将不同的单位表示为不同的类别?我个人的建议是为每个物理维度使用结构——而不是单元。所以,有一个这样的结构:
public struct Length {
public static Length FromMeters(double meters) {
// ...
}
public double InMiles() { ... }
// operator overloads
}
这样你就不会遇到泛型的任何问题,甚至不会对运行时产生影响,但仍然有编译时支持。
假设您有结构Length
、Time
和Speed
,那么您可以轻松地重载允许您划分Length 的运算符
Time
并得到 Speed
作为结果。然后,您可以通过一种方法(速度)以您喜欢的任何单位查询速度值,例如InMilesPerSecond
返回 double 值。
编辑: 我想你最初想法中的真正问题是错误抽象的问题。特别是,速度不是(在同一性意义上)长度除以时间,尽管您可以用这种方式表达。这是细微的差别。例如,您可以表示 1W = 1Nm 或 1W = 1VA。因此,您不应该为 PowerType, Length> 建模,因为这不是功率的标识,而只是一种计算方式。
关于c# - 具有泛型类型约束的泛型方法的尴尬语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28798587/