2010-10-20 13 views
9

Sto implementando i tipi di valore che rappresenta una distanza (o lunghezza). C'è un enum che rappresenta le diverse unità di misura, ad esempio:Qual è il miglior modello C# per implementare una gerarchia con un enum?

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

Queste misure rientrano in uno dei due sistemi - metrico o imperiale. Dato che enum non supporta le gerarchie, qual è il modello migliore per rappresentare questa gerarchia?

È possibile utilizzare i flag di bit? Oppure utilizzare due enumerazioni separate insieme ad alcuni metodi per correlarle? Oppure dichiarare membri statici invece di enumerazioni?

Datemi un consiglio ... come sarebbe voi implementare questo?


Edit - Altro chiarimento: Ho diverse le strutture immutabili (generato dal T4), che rappresentano varie misure:

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

... ecc, così come un codice a mano immutabili distanza, destinato a rappresentare qualsiasi misura:

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

che precedono una discussione sul fatto che la distanza deve essere convertito nell'UOM corretto chiamando il codice, l'obiettivo qui è che ToString converta il valore verso l'alto o verso il basso sulla stessa misura (imperiale o metrica) rappresentata dall'Unità corrente corrente.

Ovviamente tutto questo può essere codificato nel metodo ToString, ma dato che sto anche implementando TypeConverters e FormatProviders per questo intero shibang, mi piacerebbe trovare un modo generico per capire, da un DistanceUnit, cosa l'appropriata unità di misura next-up o next-down sarebbe.

Sto abbaiando sull'albero sbagliato volendo implementare in questo modo?

+1

userei [Flags] come lei suggerisce di implementare le categorie i bit MSB e il basso per rappresentare l'esponente. – kenny

risposta

10

Hai davvero bisogno di un enum qui? Forse, un semplice value object farà?

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 voi metodi di estensione e operatori adeguatamente definiti può ottenere una sintassi molto simile a Rubino:

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

Bella risposta. Ho già un tipo di valore per ogni unità di misura, oltre a una struttura Distanza che può rappresentare qualsiasi unità di misura. La mia domanda deriva dal fatto che, durante la creazione di rappresentazioni di stringa (es. Distance.ToString ("G")), voglio che la stringa usi l'UOM più appropriato all'interno dello stesso sistema (metrico o imperiale) ... da qui la domanda su gerarchie ed enum. BTW - non aggiunge i metodi di estensione ai tipi di valore di .NET un po 'no-no? – Mark

+0

È possibile memorizzare il tipo di UOM utilizzato per creare un'istanza di "Distanza" insieme a "millimetri" e quindi utilizzarlo per convertire in stringhe. In alternativa, puoi avere sottoclassi di 'MetricDistance',' ImperialDistance', ecc. Per quanto riguarda il no-no, sto parlando di value _objects_ (che è un pattern di progettazione), non value _types_ (che sono concetti .NET). –

2

In generale, vorrei andare con la soluzione Anton s'. Ma se l'implementazione non è possibile utilizzare tale, ed è necessario cose da usare simile a un enum, credo che questo sia un modo naturale per uso le unità:

DistanceUnit.Metric.Millimeter 
DistanceUnit.Imperial.Inch 

Al fine di utilizzarlo come che, ci dovrebbe essere:

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

Dove MetricDistanceUnit è:

public enum MetricDistanceUnit 
{ 
    Millimeter, Centimeter ... 
} 

E ImperialDistanceUnit ha il stessa struttura ..

+1

Mi piace il tuo suggerimento, anche se ho bisogno di un singolo valore-tipo che manterrà tutti i valori enum (cioè non può dividere l'enum in MetricDistancEUnit e ImperialDistanceUnit). – Mark

1

Forse tutto ciò che serve è una funzione che restituisce un'unità corrispondente sottoinsieme

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

In caso di conversione di DistanceUnit in un sistema (imperiale o metrico), sarebbe eccessivo utilizzare una SortedList o SortedSet? Come implementeresti la conversione inversa in modo efficiente? – Mark

+0

Non scappo dagli interruttori, quindi la risposta è modificata di conseguenza. – Dialecticus