使用现实世界的单位而不是类型
我有一个包含许多计算的项目,涉及很多现实世界的单位:
- 距离;
- 温度;
- 流量;
- ……
本项目涉及计算公式复杂、数量众多。
这就是为什么我认为使用温度、距离等自定义类型可以提高代码的可读性。例如:
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 类型?
I have a project with many calculations involving a lot of real world units :
- Distance;
- Temperature;
- Flow rate;
- ...
This project involves complicated and numerous calculation formulas.
That's why I supposed the use of custom types like Temperature, Distance... can be good for code readability. For example:
Temperature x = -55.3;
Meter y = 3;
or
var x = new Temperature(-55.3);
I tried to make a Temperature class that uses a double internal value.
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);
}
}
But class are nullable. This mean that something like :
Temperature myTemp;
is "correct" and will be null. I dont want this. I dont want to use structs because they are too limited :
- They cannot use parameterless constructor nor instance field intializers like
double _Value = double.Nan;
to define a default value (I wand default underlying double value to be NaN) - They cannot inherits from classes, they only can implement Interfaces
Them I wonder whether there is a way to tell C#:
Temperature myTemp = 23K; // C# does not implement anything to make K unit...
but I know C# does not handle no custom units.
Temperature myTemp = new Kelvin(23); // This might work
So I imagine I could create two Celsius and Kelvin classes that inherits from Temperature, and then I started to wonder if the idea really worth it, because it involves a lot of coding and testing.
That's the discussion I would like to start :
Would the use of real world units in my code instead of .NET types would be a good thing or not ? Did anyone this already ? What are the pitfalls and the best practices ? Or should I better keep stay away from this and use standard .NET types ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
为什么不尝试一个看起来像这样的结构:
然后像这样使用它:
Why not try a struct that looks like this:
Then use it like, say, this:
实现此目的的一种方法是使用基本对象(在您的情况下为
Temperature
)与专用于该基本对象的TemperatureTraits
类的组合。与 C++ 类比,String
等价类basic_string
实际上是一个类模板(C# 术语中的泛型),它不仅具有字符串元素(char
、wide char),同时也是一个特征类,详细说明该类对于给定类型的字符串元素(例如char_traits
)的行为方式。在您的情况下,您可以定义一个类似
public class MeasurableWithUnits
的泛型,然后实现不仅取决于可测量类,还取决于单位类。这在实践中有多有用取决于这样的对象有多少可以真正通用——在可测量和单位的组合中哪些操作是常见的?
有一篇关于 C# 特征的研究论文 这里,如果这种方法看起来很有趣。
One way to achieve this would be to use composition of the basic object (
Temperature
in your case) with aTemperatureTraits
class that specializes the basic object. By analogy to C++, theString
equivalent classbasic_string
is actually a class template (generic in C# terms) that has template parameters not only for the string element (char
, wide char) but also a traits class that elaborates on how the class behaves for a given type of string element (e.g.char_traits
).In your case, you might define a generic like
public class MeasurableWithUnits<class M MEASURABLE, class U UNITS>
and then implementation would depend not only on the measurable class but also on the units class. How useful this would be in practice would depend on how much of such an object could be made truly generic - what operations are common across combinations of
Measurable
andUnits
?There is a research paper on C# traits here, if this approach looks interesting.
我认为当您想向温度添加更多特定功能时(例如:
IsFreezing()
),它可能会很好。要解决开尔文和摄氏度的问题:创建一个接口
ITemperature
和一个基类。在基类中,您可以实现接口并填写所有类都相同的详细信息。I think it can be good when you want to add more specific functionality to a temperature (for example:
IsFreezing()
).To solve the issue with Kelvin and Celsius: make an interface
ITemperature
and a baseclass. In the baseclass, you can implement the interface and fill in the details that are the same for all classes.如果您使用结构,则它不能为
null
If you use a struct, then this cannot be
null
我认为不值得在 C# 中为单元添加静态类型。您将需要重载如此多的运算符(对于所有单位组合,而不仅仅是所有单位)。并且像 Math.Sqrt 这样的函数可以在普通双精度上工作,...
您可能会尝试使用动态类型:
然后在调试模式下编译时添加检查单元是否适合在一起。在发布中,只需删除 PhysicalUnit 字段和所有检查,您(几乎)与使用普通双精度的代码一样快。
I don't think it's worth adding static types for units in C#. You would need to overload so many operators(for all unit combinations, not just for all units). And build in functions like Math.Sqrt work on normal doubles,...
What you might try is using dynamic types:
And then when compiling in debug mode add checks if the units fit together. And in release just remove the PhysicalUnit field and all the checks, and you're (almost) as fast as code using normal doubles.
我将使
Temperature
成为一个抽象类,将温度(以开尔文为单位!)存储在 InternalTemperature 属性中。派生类
Celcius
会将输入值内部转换为开尔文。它有一个(只读)Value 属性,可以将内部值转换回来。然后比较它们(一个比另一个温暖)就会很容易。
I would make
Temperature
an abstract class that stores the temperature (in Kelvin!) in an InternalTemperature property.A derived class
Celcius
would translate the input value internaly to Kelvin. It would have a (readonly) Value property that translated the internal value back.Comparing them (is one warmer than the other) would then be easy.