2010-10-20 36 views
9

Estoy implementando tipos de valores que representan una distancia (o longitud). Hay una enumeración que representa las diferentes unidades de medida, por ejemplo:¿Cuál es el mejor patrón de C# para implementar una jerarquía con una enumeración?

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

Estas mediciones caen en una de dos sistemas - ya sea métrico o imperial. Dado que enum no admite jerarquías, ¿cuál es el mejor patrón para representar esta jerarquía?

¿Es para usar banderas de bits? ¿O usa dos enumeraciones separadas junto con algunos métodos para correlacionarlas? O para declarar miembros estáticos en lugar de enumeraciones?

Dame un consejo ... ¿cómo sería que implementar esto?


Editar - Más aclaración: Tengo varias estructuras inmutables (generada por T4) que representan diversas medidas:

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í como una distancia inmutable codificado a mano, la intención de representar cualquier medida:

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

Renunciando a una discusión acerca de si la distancia debe ser convertida a la UOM correcta mediante el código de llamada, el objetivo aquí es que ToString convierta el valor hacia arriba o hacia abajo en la misma medición (imperial o métrica) que se representa con el UnitOfMeasure actual.

Obviamente, todo esto podría estar codificado en el método ToString, pero dado que también estoy implementando TypeConverters y FormatProviders para todo este shibang, me gustaría encontrar un medio genérico para descifrar, desde una DistanceUnit, qué la unidad de medida apropiada siguiente o posterior sería.

¿Estoy ladrando al árbol equivocado al querer aplicar de esta manera?

+1

yo usaría [Banderas] como sugieres para implementar las categorías en los bits MSB y el bajo con el fin de representar el exponente. – kenny

Respuesta

10

lo que realmente necesita un enum aquí? Tal vez, un simple value object va a hacer?

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

con ustedes los métodos de extensión y operadores definidos adecuadamente puede ser muy similar a la sintaxis de Ruby:

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

Buena respuesta. Ya tengo un tipo de valor para cada unidad de medida, así como una estructura de distancia que puede representar cualquier unidad de medida. Mi pregunta se basa en el hecho de que, al crear representaciones de cadenas (es decir, Distance.ToString ("G")), quiero que la cadena use la UOM más apropiada dentro del mismo sistema (métrico o imperial) ... de ahí la pregunta sobre jerarquías y enum. Por cierto, ¿no está agregando los métodos de extensión a los tipos de valor de .NET un poco de no-no? – Mark

+0

Puede almacenar el tipo de UOM utilizado para crear una instancia de 'Distancia' junto con' milímetros' y luego usarlo para convertir a cadenas. Alternativamente, puede tener una subclase 'MetricDistance',' ImperialDistance', etc. En cuanto a un no-no, estoy hablando de value _objects_ (que es un patrón de diseño), no de value _types_ (que son conceptos de .NET). –

2

En términos generales, me gustaría ir con solución Anton 's. Pero si su aplicación no puede utilizar eso, y lo que necesita cosas para ser usados ​​similar a una enumeración, Creo que esta es una forma natural para uso las unidades:

DistanceUnit.Metric.Millimeter 
DistanceUnit.Imperial.Inch 

Con el fin de utilizarlo como que, no debe haber:

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

Dónde MetricDistanceUnit es:

public enum MetricDistanceUnit 
{ 
    Millimeter, Centimeter ... 
} 

Y tiene el ImperialDistanceUnit misma estructura ...

+1

Me gusta su sugerencia, aunque necesito un tipo de valor único que contendrá todos los valores enum (es decir, no puede dividir la enumeración en MetricDistancEUnit y ImperialDistanceUnit). – Mark

1

Tal vez todo lo que necesita es una función que devuelve un subconjunto correspondiente unidad

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; 
    } 
    } 
} 
+0

Al convertir la DistanceUnit a un sistema (Imperial o Métrico), ¿sería excesivo utilizar SortedList u SortedSet? ¿Cómo implementaría la conversión inversa de manera eficiente? – Mark

+0

No huyo de los conmutadores, por lo que la respuesta se edita en consecuencia. – Dialecticus

Cuestiones relacionadas