2010-10-20 9 views
9

Ich implementiere Werttypen, die eine Entfernung (oder Länge) darstellt. Es gibt eine ENUM, der die verschiedenen Masseinheiten darstellt, zB:Was ist das beste C# -Muster für die Implementierung einer Hierarchie mit einer Enumeration?

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

Diese Messungen lassen sich in einem der beiden Systeme - metrische oder imperiale. Da Enum Hierarchien nicht unterstützt, was ist das beste Muster für die Darstellung dieser Hierarchie?

Verwenden Sie Bit-Flags? Oder verwenden Sie zwei separate Enums zusammen mit einigen Methoden, um sie zu korrelieren? Oder um statische Mitglieder anstelle von enums zu deklarieren?

Geben Sie mir einen Ratschlag ... wie würde Sie dies implementieren?


Bearbeiten - Weitere Klarstellung: ich mehrere unveränderliche Strukturen haben (durch T4 erzeugt), die verschiedene Messungen darstellen:

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, sowie eine handcodierte unveränderliche Entfernung, beabsichtigt jede Maßnahme darstellen:

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

eine Diskussion über Verzicht, ob die Entfernung sein sollte Durch das Aufrufen von Code in die richtige UOM konvertiert, besteht das Ziel darin, dass ToString den Wert auf derselben Messung (imperial oder metrisch) nach oben oder unten konvertiert, die durch die aktuelle UnitOfMeasure dargestellt wird.

Offensichtlich könnte dies alles hart in die ToString-Methode codiert werden, aber da ich auch TypeConverters und FormatProviders für dieses ganze Shibang implementiere, würde ich gerne ein generisches Mittel finden, aus einer DistanceUnit herauszufinden, was die entsprechende nächst- oder nächstniedrigere Maßeinheit wäre.

Banne ich den falschen Baum an, indem ich auf diese Weise implementieren möchte?

+1

kehrt würde ich [Flags] verwenden, wie Sie vorschlagen, die Kategorien in den MSB-Bits und den niederwertigen zu implementieren den Exponenten darzustellen. – kenny

Antwort

10

Brauchen Sie wirklich eine enum hier? Vielleicht, eine einfache value object wird tun?

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

Mit Erweiterungsmethoden Sie und korrekt definierten Operatoren können sehr Rubin-ähnliche Syntax erhalten:

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

Schöne Antwort. Ich habe bereits einen Werttyp für jede Maßeinheit sowie eine Distanzstruktur, die eine beliebige Maßeinheit darstellen kann. Meine Frage rührt von der Tatsache her, dass ich bei der Erstellung von String-Repräsentationen (zB Distance.ToString ("G")) die Zeichenfolge die am besten geeignete UOM innerhalb des gleichen Systems (metrisch oder imperial) verwenden soll ... daher die Frage über Hierarchien und enum. BTW - Hinzufügen von Erweiterungsmethoden zu .NET-Werttypen nicht ein bisschen ein Nein-Nein? – Mark

+0

Sie können den UOM-Typ speichern, der zum Erstellen einer Instanz von "Distance" zusammen mit "Millimeters" verwendet wird, und ihn dann zum Konvertieren in Strings verwenden. Alternativ können Sie die Unterklassen "MetricDistance", "ImperialDistance" usw. verwenden. Wie für ein No-No, ich spreche über Wert _objects_ (das ist ein Design-Muster), nicht Wert _types_ (die .NET-Konzepte sind). –

2

Generell würde ich mit Anton ‚s Lösung gehen. Aber wenn Ihre Implementierung kann das nicht verwenden, und Sie müssen Dinge zu einem Enum ähnlich verwendet werden, Ich denke, dies ist eine natürliche Art und Weise zu Verwendung die Einheiten:

DistanceUnit.Metric.Millimeter 
DistanceUnit.Imperial.Inch 

Um es zu benutzen, wie dass, sollte es sein:

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

Wo MetricDistanceUnit ist:

public enum MetricDistanceUnit 
{ 
    Millimeter, Centimeter ... 
} 

Und ImperialDistanceUnit hat die gleiche Struktur ..

+1

Ich mag Ihren Vorschlag, obwohl ich einen einzigen Wert-Typ benötigen, der alle Enum-Werte enthalten wird (d. H. Kann die Enumeration nicht in MetricDistancEUnit und ImperialDistanceUnit aufteilen). – Mark

1

alles, was Sie vielleicht brauchen, ist eine Funktion, die eine entsprechende Einheit Teilmenge

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

Wenn Sie die DistanceUnit zurück in ein System (Imperial oder Metric) konvertieren, wäre es dann übertrieben, eine SortedList oder SortedSet zu verwenden? Wie würden Sie die Rückumwandlung effizient umsetzen? – Mark

+0

Ich laufe nicht weg von Schaltern, also wird die Antwort entsprechend bearbeitet. – Dialecticus

Verwandte Themen