我有一个涉及许多现实世界单位的计算项目:
这个项目涉及复杂而繁多的计算公式。
这就是为什么我认为使用 Temperature , Distance 等自定义类型可以提高代码的可读性。例如:
Temperature x = -55.3;
Meter y = 3;
或
var x = new Temperature(-55.3);
我尝试制作一个使用双重内部值的Temperature类。
public class Temperature
{
double _Value = double.NaN;
public Temperature() { }
public Temperature(double v) {
_Value = v;
}
public static implicit operator Temperature(double v) {
return new Temperature(v);
}
}
但是类可以为空。这意味着:
Temperature myTemp;
是“正确的”并且将为null。我不想要这个。我不想使用结构,因为它们太有限了:
double _Value = double.Nan;
这样的实例字段初始化来定义默认值(我的默认底层double值为NaN)我想知道是否有办法告诉C#:
Temperature myTemp = 23K; // C# does not implement anything to make K unit...
但我知道C#不处理任何自定义单位。
Temperature myTemp = new Kelvin(23); // This might work
所以我想我可以创建两个继承温度的Celsius和Kelvin类,然后我开始怀疑这个想法是否真的值得,因为它涉及大量的编码和测试。
这是我想要开始的讨论:
在我的代码中使用真实世界单元而不是.NET类型是否是一件好事?有人这个吗?有哪些陷阱和最佳做法?或者我应该更好地远离这个并使用标准的.NET类型?
答案 0 :(得分:10)
为什么不尝试一下这样的结构:
/// <summary>
/// Temperature class that uses a base unit of Celsius
/// </summary>
public struct Temp
{
public static Temp FromCelsius(double value)
{
return new Temp(value);
}
public static Temp FromFahrenheit(double value)
{
return new Temp((value - 32) * 5 / 9);
}
public static Temp FromKelvin(double value)
{
return new Temp(value - 273.15);
}
public static Temp operator +(Temp left, Temp right)
{
return Temp.FromCelsius(left.Celsius + right.Celsius);
}
private double _value;
private Temp(double value)
{
_value = value;
}
public double Kelvin
{
get { return _value + 273.15; }
}
public double Celsius
{
get { return _value; }
}
public double Fahrenheit
{
get { return _value / 5 * 9 + 32; }
}
}
然后使用它,比如说:
static void Main(string[] args)
{
var c = Temp.FromCelsius(30);
var f = Temp.FromFahrenheit(20);
var k = Temp.FromKelvin(20);
var total = c + f + k;
Console.WriteLine("Total temp is {0}F", total.Fahrenheit);
}
答案 1 :(得分:1)
实现此目的的一种方法是使用基本对象(在您的情况下为Temperature
)的组合与专门用于基本对象的TemperatureTraits
类。类似于C ++,String
等效类basic_string
实际上是一个类模板(C#术语中的通用),它不仅具有模板参数,而且还包含字符串元素(char
,宽字符),但是还有一个traits类,详细说明了类对于给定类型的字符串元素的行为(例如char_traits
)。
在您的情况下,您可以定义类似
的通用名称 public class MeasurableWithUnits<class M MEASURABLE, class U UNITS>
然后实现不仅取决于可测量的类,还取决于单元类。这在实践中有多大用处取决于这样一个对象有多少可以真正通用 - 在Measurable
和Units
的组合中哪些操作是常见的?
有一篇关于C#traits here的研究论文,如果这种方法看起来很有趣。
答案 2 :(得分:0)
我认为当您想要为温度添加更多特定功能时可能会很好(例如:IsFreezing()
)。
要解决Kelvin和Celsius的问题:创建一个接口ITemperature
和一个基类。在基类中,您可以实现接口并填写所有类的相同细节。
答案 3 :(得分:0)
如果您使用结构,那么这不能是null
struct Temperature
{
double _Value;
}
答案 4 :(得分:0)
我认为在C#中为单位添加静态类型是不值得的。您需要重载这么多运算符(对于所有单位组合,而不仅仅是所有单位)。并建立像Math.Sqrt这样的函数在普通双精度上工作,......
您可能会尝试使用动态类型:
class PhysicalUnit
{
}
struct PhysicalValue
{
readonly Value;
readonly PhysicalUnit;
}
然后在调试模式下编译时,添加检查单元是否适合。在发布中,只需删除PhysicalUnit字段和所有检查,您(几乎)与使用普通双精度的代码一样快。
答案 5 :(得分:0)
我会将Temperature
设为一个抽象类,用于将温度(以开尔文为单位)存储在InternalTemperature属性中。
派生类Celcius
会将输入值内部转换为开尔文。它将具有(只读)Value属性,可以将内部值转换回来。
比较它们(比另一个更温暖)会很容易。