2010-10-20 6 views
9

J'implémente des types de valeurs qui représentent une distance (ou une longueur). Il y a un ENUM qui représente les différentes unités de mesure, par exemple:Quel est le meilleur modèle C# pour implémenter une hiérarchie avec une énumération?

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

Ces mesures entrent dans l'un des deux systèmes - soit métrique ou impérial. Puisque enum ne supporte pas les hierachies, quel est le meilleur modèle pour représenter cette hiérarchie?

Est-ce d'utiliser des drapeaux de bit? Ou utiliser deux enums séparés avec quelques méthodes pour les corréler? Ou pour déclarer des membres statiques au lieu d'enums? Donnez-moi quelques conseils ... comment vous mettre en œuvre cela?


Edition - Plus d'éclaircissements: J'ai plusieurs struct immuables (généré par T4), qui représentent différentes mesures:

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, ainsi qu'une distance immuable code à la main, destiné à représenter toute mesure:

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

Renoncer une discussion pour savoir si la distance doit être converti en UOM correcte par le code appelant, ToString a pour but de convertir la valeur vers le haut ou vers le bas sur la même mesure (impériale ou métrique) représentée par le UnitOfMeasure actuel.

Il est évident que tout ceci pourrait être codé en dur dans la méthode ToString, mais étant donné que j'implémente aussi TypeConverters et FormatProviders pour tout ce shibang, j'aimerais trouver un moyen générique de déterminer, à partir d'un DistanceUnit, l'unité de mesure appropriée suivante ou suivante serait. Suis-je aboyer le mauvais arbre en voulant mettre en œuvre de cette manière?

+1

J'utiliser [Flags] comme vous le suggérez de mettre en œuvre les catégories dans les bits MSB et le faible pour représenter l'exposant. – kenny

Répondre

10

Avez-vous vraiment besoin d'un enum ici? Peut-être, un simple value object va faire?

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

Avec des méthodes d'extension vous et les opérateurs bien définis peuvent être très Ruby comme syntaxe:

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

Bonne réponse. J'ai déjà un type de valeur pour chaque unité de mesure, ainsi qu'une structure de distance qui peut représenter n'importe quelle unité de mesure. Ma question provient du fait que, lors de la création de représentations de chaînes (ie Distance.ToString ("G")), je veux que la chaîne utilise l'UOM la plus appropriée qui soit dans le même système (métrique ou impérial) ... d'où la question à propos des hiérarchies et enum. BTW - ne pas ajouter des méthodes d'extension aux types de valeur de .NET un peu d'un non-non? – Mark

+0

Vous pouvez stocker le type d'UOM utilisé pour créer une instance de 'Distance' avec' millimeters', puis l'utiliser pour convertir en chaînes. Alternativement, vous pouvez avoir une sous-classe 'MetricDistance',' ImperialDistance', etc. Comme pour un non-non, je parle de la valeur _objects_ (qui est un modèle de conception), pas de valeur _types_ (qui sont des concepts .NET). –

2

D'une manière générale, j'irais avec une solution de Anton. Mais si votre mise en œuvre ne peut pas l'utiliser, et vous avez besoin des choses à utiliser semblable à un ENUM, Je pense que cela est un moyen naturel de utiliser les unités:

DistanceUnit.Metric.Millimeter 
DistanceUnit.Imperial.Inch 

Pour l'utiliser comme que, il devrait y avoir:

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

MetricDistanceUnit est:

public enum MetricDistanceUnit 
{ 
    Millimeter, Centimeter ... 
} 

Et ImperialDistanceUnit a la même structure ..

+1

J'aime votre suggestion, bien que j'ai besoin d'un seul type de valeur qui contiendra toutes les valeurs enum (c'est-à-dire ne peut pas diviser l'enum en MetricDistancEUnit et ImperialDistanceUnit). – Mark

1

Peut-être tout ce que vous avez besoin est une fonction qui retourne un sous-ensemble de l'unité correspondante

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

En convertissant le DistanceUnit à un système (impérial ou métrique), serait-il exagéré d'utiliser une SortedList ou SortedSet? Comment mettriez-vous en œuvre la conversion inverse efficacement? – Mark

+0

Je ne fuis pas les commutateurs, donc la réponse est éditée en conséquence. – Dialecticus

Questions connexes