我应该使用什么模式来表示分层枚举
本文关键字:表示 分层 枚举 模式 什么 我应该 | 更新日期: 2023-09-27 18:35:38
我正在试验一个在给定时间(值和时间元组)发布值的API。这些样本将由数据查看器(例如图表)使用。
我想将该值与数量和单位相关联,例如以米为单位的长度。这样我的"查看器"就可以适当地缩放它。
我正在寻找一种分层枚举,如下所示:
enum Quantity
{
Mass.Kg,
Mass.g,
Length.m,
Length.mm
}
但这在 C# 中不存在。
我不确定表达这一点的最佳模式,我想出了以下内容。有没有公认的或更好的方法来做到这一点?
using System;
using Moq;
namespace ConsoleApplication26
{
class Program
{
static void Main(string[] args)
{
//use a Mock to play with the API
Mock<ITelemetryPublisherFactory> mockTelemetryPublisherFactory = new Mock<ITelemetryPublisherFactory>();
var telemetryPublisherFactory = mockTelemetryPublisherFactory.Object;
//example usages
var massTelemetryPublisher = telemetryPublisherFactory.GetChannelSamplePublisher<Double>("My Mass", Mass.Kg);
massTelemetryPublisher.PublishChannelSampleAtTimeNow(83.4);
var lengthTelemetryPublisher = telemetryPublisherFactory.GetChannelSamplePublisher<Int32>("My Height", Length.μm);
lengthTelemetryPublisher.PublishChannelSampleAtTimeNow(1800000);
//10 years time..
lengthTelemetryPublisher.PublishChannelSampleAtTimeNow(1800000);
massTelemetryPublisher.PublishChannelSampleAtTimeNow(120.1);
}
}
public interface ITelemetryPublisherFactory
{
ITelemetryPublisher<T> GetChannelSamplePublisher<T>(String channelName, Quantity quantity);
}
public interface ITelemetryPublisher<T>
{
void PublishChannelSampleAtTimeNow(T sampleValue);
}
public abstract class Quantity {}
public class Mass : Quantity
{
private enum Unit
{
g,
Kg
}
private readonly Unit _unit;
private Mass(Unit unit)
{
_unit = unit;
}
public static Quantity Kg {get { return new Mass(Unit.Kg); }}
public static Quantity g { get { return new Mass(Unit.g); } }
public override string ToString()
{
return String.Format("Mass.{0}", _unit);
}
}
public class Length : Quantity
{
private enum Unit
{
m,
mm,
μm,
beardSecond
}
private readonly Unit _unit;
private Length(Unit unit)
{
_unit = unit;
}
public static Quantity m { get { return new Length(Unit.m); } }
public static Quantity mm { get { return new Length(Unit.mm); } }
public static Quantity μm { get { return new Length(Unit.μm); } }
public static Quantity beardSecond { get { return new Length(Unit.beardSecond); } }
public override string ToString()
{
return String.Format("Length.{0}", _unit);
}
}
}
我认为最好为度量单位创建一个Unit
类,并为度量单位创建一个将度量单位与金额相关联的Quantity
类。查看想法的数量模式。由于您还希望记录度量单位的"类型",因此可以创建一个记录该信息的UnitType
类:
public sealed partial class UnitType {
public string Name { get; private set; }
public UnitType(string name) {
Name = name;
}
}
public sealed partial class Unit {
public string Name { get; private set; }
public UnitType Type { get; private set; }
public Unit(string name, UnitType type) {
Name = name;
Type = type;
}
}
(您应该通过覆盖Equals
和GetHashCode
使它们成为正确的值类型)
Unit
类可以扩展以提供例如转换、复合单元、格式化和解析。
然后,您可以在类中定义常见情况:
public partial class UnitType {
public static readonly UnitType Mass = new UnitType("Mass");
public static readonly UnitType Length = new UnitType("Length");
}
public partial class Unit {
public static readonly Unit Grams = new Unit("g", UnitType.Mass);
public static readonly Unit Kilos = new Unit("kg", UnitType.Mass);
// ...
}
或者用静态类定义你的"层次结构":
public static class Mass {
public static readonly UnitType Type = new UnitType("Mass");
public static readonly Unit Grams = new Unit("g", Type);
public static readonly Unit Kilos = new Unit("kg", Type);
...
}
public static class Length ...
Quantity
类也将是不可变的值类型(仅显示其用法):
var eniacWeight = new Quantity(27, Mass.Tons);
或者,您可以使用扩展方法创建Quantity
:
var eniacWeight = 27.Tons();
(来自ENIAC)
是不可能的。枚举是基元类型,不能从其他枚举继承,因为继承是对象的属性。
,分层枚举是不可能的。但是,如果您只使用指标,则可以使用标准前缀(如果有帮助)。
enum MeasurementUnits
{
Gram,
Metre,
Litre,
Hectare
// etc
}
enum MeasurementPrefix
{
Milli,
Natural,
Kilo,
Mega
// etc
}
这可能不是您想要的,但它将提供您可能正在寻找的"分组"类型(例如,通过检查其"单位"值来对长度、重量等进行分组测量)。
你建议的方法对我来说似乎是合理的,我在我的一个项目中使用了类似的东西。但是,我保留了对象的实际值部分,并且我使用struct
而不是class
,因为它们自然是值类型。继承在这里不是必需的(无论如何,结构也是不可能的),所以我使用接口来创建合约并在需要时充当约束(我称之为IUnitOfMeasure
)。
我不建议创建一个将各种类型测量的所有单位组合在一起的枚举;验证单位以确保有人在使用长度时没有引用质量单位是地狱。
public interface IUnitOfMeasure<TThis>
where TThis : IUnitOfMeasure<TThis>
{
TThis ConvertTo(TThis value);
}
public struct Mass : IUnitOfMeasure<Mass>
{
public enum Units
{
Gram,
Kilogram
}
private double _value;
private Mass.Units _unit;
public double Value { get { return _value; } }
public Mass.Units Unit { get { return _unit; } }
public Mass(double value, Mass.Units unit)
{
_value = value;
_unit = unit;
}
public Mass ConvertTo(Mass value)
{
switch(value.Unit)
{
case Units.Gram:
return new Mass(Unit == Units.Gram ? Value : Value/1000, Units.Gram);
case Units.Kilogram:
return new Mass(Unit == Units.Gram ? Value*1000 : Value, Units.Kilogram);
default:
throw new NotImplementedException();
}
}
public override string ToString()
{
return string.Format("{0} {1}", Value, Unit);
}
public static readonly Mass G = new Mass(0, Units.Gram);
public static readonly Mass Kg = new Mass(0, Units.Kilogram);
}
用法:
var kg = new Mass(5.0, Mass.Units.Kilogram);
Console.WriteLine(kg); // writes "5 Kilogram"
var g = kg.ConvertTo(Mass.G);
Console.WriteLine(g); // writes ".005 Gram"
如果您不关心保留值,而只想将枚举/静态值保留在中心位置:
public static class UnitOfMeasure
{
public enum Mass
{
Gram,
Kilogram
}
public enum Length
{
Meter,
Kilometer
}
// etc.
}
用法:var unit = UnitOfMeasure.Mass.Kilogram;
不能使用枚举引入继承。枚举只是一种方便的机制,允许您在代码中使用有意义的文本标识符。从您拥有的代码中,我建议您使用类似枚举;
public enum UnitOfMeasure
{
MassGrams,
MassKg,
LengthMM,
LengthCM,
. . .
}
或者将其拆分到适当的位置,例如,分别定义质量和长度。
"继承"只是你在思考这个问题时引入的东西,但它对你的解决方案不是必需的。当你想处理质量时,你只看适合质量的标志/枚举。