Какой лучший шаблон C # для реализации иерархии с перечислением? - PullRequest
9 голосов
/ 20 октября 2010

Я реализую типы значений, которые представляют расстояние (или длину). Существует перечисление, которое представляет различные единицы измерения, например:

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

Эти измерения относятся к одной из двух систем - метрической или имперской. Поскольку enum не поддерживает иерархии, каков наилучший шаблон для представления этой иерархии?

Это использовать битовые флаги? Или использовать два отдельных перечисления вместе с некоторыми методами для их корреляции? Или объявить статические члены вместо перечислений?

Дайте мне совет ... как бы вы реализовали это?


Редактировать - Больше разъяснений: У меня есть несколько неизменных структур (генерируемых 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 ....

... и т. Д., А также неизменяемое расстояние с ручным кодированием, предназначенное для представления любого показателя:

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
    }
}

Прежде чем обсуждать, следует ли преобразовывать расстояние в правильную единицу измерения с помощью вызывающего кода, цель состоит в том, чтобы ToString преобразовал значение вверх или вниз в том же измерении (имперском или метрическом), которое представлено текущим модулем UnitOfMeasure .

Очевидно, что все это может быть жестко запрограммировано в метод ToString, но, учитывая, что я также реализую TypeConverters и FormatProviders для всего этого сибанга, я хотел бы найти общий способ выяснить из DistanceUnit, что подходящей следующей или следующей единицей измерения будет.

Я лаю не на том дереве, желая реализовать таким образом?

Ответы [ 3 ]

10 голосов
/ 20 октября 2010

Вам действительно нужен enum здесь? Может быть, подойдет простой объект значения ?

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);
    }
}

С помощью методов расширения вы и правильно определенные операторы можете получить очень похожий на Ruby синтаксис:

var theWholeNineYards = 9.Yards() + 34.Inches();
2 голосов
/ 20 октября 2010

В общем, я бы пошел с решением Антона . Но если ваша реализация не может использовать это, и вам нужно использовать вещи, похожие на перечисление, Я думаю, что это естественный способ использования единиц:

DistanceUnit.Metric.Millimeter  
DistanceUnit.Imperial.Inch  

Чтобы использовать его таким образом, должно быть:

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

Где MetricDistanceUnit:

public enum MetricDistanceUnit  
{
   Millimeter, Centimeter ...
}

А ImperialDistanceUnit имеет такую ​​же структуру ..

1 голос
/ 20 октября 2010

Может быть, все, что вам нужно, это функция, которая возвращает соответствующее подмножество единиц

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;
    }
  }
}
...