10

我有一个项目,其中涉及许多实际单位的计算:

  • 距离;
  • 温度;
  • 流速;
  • ...

该项目涉及复杂且众多的计算公式。

这就是为什么我认为使用温度距离等自定义类型可以提高代码的可读性。例如:

Temperature x = -55.3;
Meter y = 3;

或者

var x = new Temperature(-55.3);

我试图制作一个使用双内部值的温度类。

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;

是“正确的”并且将为空。我不想要这个。我不想使用结构,因为它们太有限了:

  • 他们不能使用无参数构造函数,也不能使用实例字段初始化double _Value = double.Nan;器来定义默认值(我希望默认的基础双精度值是 NaN)
  • 它们不能从类继承,只能实现接口

他们我想知道是否有办法告诉 C#:

Temperature myTemp = 23K; // C# does not implement anything to make K unit...

但我知道 C# 不处理任何自定义单位。

Temperature myTemp = new Kelvin(23); // This might work

所以我想我可以创建两个继承自 Temperature 的 Celsius 和 Kelvin 类,然后我开始怀疑这个想法是否真的值得,因为它涉及大量的编码和测试。

这就是我想开始的讨论:

在我的代码中使用真实世界的单位而不是 .NET 类型会是一件好事吗?有人已经这样做了吗?有哪些陷阱和最佳实践?或者我应该最好远离这个并使用标准的 .NET 类型?

4

6 回答 6

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);
    }
于 2010-10-22T10:40:58.250 回答
1

实现此目的的一种方法是将基本对象(Temperature在您的情况下)与TemperatureTraits专门化基本对象的类一起使用。与 C++ 类比,String等效类basic_string实际上是一个类模板(在 C# 术语中是通用的),它不仅具有字符串元素 ( char, 宽字符) 的模板参数,而且还具有详细说明类如何针对给定类型的行为的特征类字符串元素(例如char_traits)。

在您的情况下,您可能会定义一个通用的

public class MeasurableWithUnits<class M MEASURABLE, class U UNITS>

然后实现不仅取决于可测量的类,还取决于单位类。这在实践中会有多大用处取决于有多少这样的对象可以真正通用 - 哪些操作在 和 的组合中是常见MeasurableUnits

如果这种方法看起来很有趣,这里有一篇关于 C# 特征的研究论文。

于 2010-10-22T10:52:19.307 回答
0

我认为当您想为温度添加更多特定功能时会很好(例如:)IsFreezing()

要解决开尔文和摄氏温度的问题:创建一个接口ITemperature和一个基类。在基类中,您可以实现接口并填写所有类都相同的细节。

于 2010-10-22T10:32:46.817 回答
0

如果您使用结构,那么这不能null

struct Temperature 
{ 
    double _Value; 
} 
于 2010-10-22T10:33:56.003 回答
0

我认为在 C# 中为单元添加静态类型不值得。您需要重载这么多运算符(对于所有单位组合,而不仅仅是所有单位)。并内置诸如 Math.Sqrt 之类的函数适用于普通双打,...

您可能会尝试使用动态类型:

class PhysicalUnit
{
}

struct PhysicalValue
{
    readonly Value;
    readonly PhysicalUnit;
}

然后在调试模式下编译时添加检查单元是否适合在一起。在发布时,只需删除 PhysicalUnit 字段和所有检查,您(几乎)与使用普通双打的代码一样快。

于 2010-10-22T10:34:00.480 回答
0

我会创建Temperature一个抽象类,将温度(以开尔文为单位!)存储在 InternalTemperature 属性中。

派生类Celcius会将输入值内部转换为开尔文。它将有一个(只读)Value 属性,将内部值翻译回来。

比较它们(一个比另一个暖)就很容易了。

于 2010-10-22T10:48:57.647 回答