简体   繁体   English

使用枚举实现层次结构的最佳C#模式是什么?

[英]What's the best C# pattern for implementing a hierarchy with an enum?

I'm implementing value types which represents a distance (or length). 我正在实现代表距离(或长度)的值类型。 There's an enum that represents the different units of measure, eg: 有一个枚举代表不同的度量单位,例如:

public enum DistanceUnit 
{
   Millimeter,
   Centimeter,
   Meter,
   Kilometer,
   Inch,
   Foot,
   Yard,
   Mile
};

These measurements fall into one of two systems - either metric or imperial. 这些测量属于两个系统中的一个 - 公制或英制。 Since enum doesn't support hierachies, what's the best pattern for representing this hierarchy? 由于枚举不支持hierachies,表示这种层次结构的最佳模式是什么?

Is it to use bit flags? 它是否使用位标志? Or use two seperate enums along with some methods to correlate them? 或者使用两个单独的枚举以及一些方法来关联它们? Or to declare static members instead of enums? 或者声明静态成员而不是枚举?

Give me some advice... how would you implement this? 给我一些建议...... 将如何实现这一点?


Edit - More clarification: I have several immutable structs (generated by T4) which represent various measurements: 编辑 - 更多说明:我有几个不可变的结构(由T4生成)代表各种测量:

public struct Meters : IEquatable<Distance>, 
                       IEquatable<Meters>, 
                       IEquatable<Millimeters>, ... IComparable<> etc.. etc..
{
    public readonly decimal Value;
    ...
    public static implicit operator Distance (Meters other) {
        // Can implicitly cast to Distance
    }
}

public struct Millimeters ...
public struct Centimeters ....

... etc, as well as a hand-coded immutable Distance, intended to represent any measure: ...等,以及手动编码的不可变距离,旨在表示任何度量:

public struct Distance : IEquatable<Distance>, 
                       IEquatable<Meters>, 
                       IEquatable<Millimeters>, ...
                       IFormattable
{

    public readonly decimal Value;
    public readonly DistanceUnits UnitOfMeasure;
    ...
    public string ToString(string format, IFormatProvider provider)
    {
        // Logic:
        // If UOM = Meters and Value < .1 then display as "10 cm" instead of ".1 M"
        // If UOM = Inches and value multiple of 12, display as feet
        // etc, etc
    }
}

Foregoing a discussion about whether the Distance should be converted to the correct UOM by calling code, the goal here is for ToString to convert the value up or down on the same measurement (imperial or metric) that is represented by the current UnitOfMeasure. 在讨论是否应通过调用代码将距离转换为正确的UOM时,此处的目标是让ToString在由当前UnitOfMeasure表示的相同度量(英制或公制)上向上或向下转换值。

Obviously this could all be hard coded into the ToString method, but given that I'm also implementing TypeConverters and FormatProviders for this whole shibang, I'd like to find a generic means of figuring out, from a DistanceUnit, what the appropriate next-up or next-down unit of measure would be. 显然这可以全部硬编码到ToString方法中,但鉴于我也在为整个shibang实现TypeConverters和FormatProviders,我想找到一种通用的方法,从DistanceUnit中找出适当的下一个 - 上行或下行单位将是。

Am I barking up the wrong tree by wanting to implement in this manner? 我是不是想以这种方式实施错误的树?

Do you really need an enum here? 你真的需要一个enum吗? Maybe, a simple value object will do? 也许,一个简单的价值对象会做什么?

public class Distance
{
    private readonly decimal millimeters;

    public decimal Meters
    { 
        get { return millimeters * 0.001m; } 
    }

    private Distance(decimal millimeters)
    {
        this.millimeters = millimeters;
    }

    public static Distance Yards(decimal yards)
    {
        return new Distance(yards * 914.4m);
    }
}

With extension methods you and properly defined operators can get very Ruby-like syntax: 使用扩展方法,您和正确定义的运算符可以获得类似Ruby的语法:

var theWholeNineYards = 9.Yards() + 34.Inches();

Generally speaking, I would go with Anton 's solution. 一般来说,我会选择安东的解决方案。 But if your implementation can't use that, and you need things to be used similar to an enum, I think this is a natural way to use the units: 但是如果你的实现不能使用它,并且你需要使用类似于枚举的东西,我认为这是使用单位的自然方式:

DistanceUnit.Metric.Millimeter  
DistanceUnit.Imperial.Inch  

In order to use it like that, there should be: 为了像这样使用它,应该有:

public static class DistanceUnit  
{
  public static MetricDistanceUnit Metric;
  public static ImperialDistanceUnit Imperial;
}   

Where MetricDistanceUnit is: MetricDistanceUnit是:

public enum MetricDistanceUnit  
{
   Millimeter, Centimeter ...
}

And ImperialDistanceUnit has the same structure.. ImperialDistanceUnit具有相同的结构..

Maybe all you need is a function that returns a corresponding unit subset 也许您需要的只是一个返回相应单元子集的函数

class UnitSystem
{
  public enum Type
  {
    Metric,
    Imperial
  }

  public static DistanceUnit[] GetUnits(Type type)
  {
    switch (type)
    {
      case Type.Metric:
        return new DistanceUnit[] {
          DistanceUnit.Millimeter,
          DistanceUnit.Centimeter,
          DistanceUnit.Meter,
          DistanceUnit.Kilometer
        }

      case Type.Imperial:
        return new DistanceUnit[] {
          DistanceUnit.Inch,
          DistanceUnit.Foot,
          DistanceUnit.Yard,
          DistanceUnit.Mile
        }
    }
  }

  public static Type GetType(DistanceUnit unit)
  {
    switch (unit)
    {
      case DistanceUnit.Millimeter:
      case DistanceUnit.Centimeter:
      case DistanceUnit.Meter:
      case DistanceUnit.Kilometer:
        return Type.Metric;

      case DistanceUnit.Inch:
      case DistanceUnit.Foot:
      case DistanceUnit.Yard:
      case DistanceUnit.Mile:
        return Type.Imperial;
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM