views:

133

answers:

3

I'm implementing value types which represents a distance (or length). There's an enum that represents the different units of measure, eg:

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

These measurements fall into one of two systems - either metric or imperial. Since enum doesn't support hierachies, what's the best pattern for representing this hierarchy?

Is it to use bit flags? Or use two seperate enums along with some methods to correlate them? Or to declare static members instead of enums?

Give me some advice... how would you implement this?


Edit - More clarification: I have several immutable structs (generated by T4) which represent various measurements:

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 well as a hand-coded immutable Distance, intended to represent any measure:

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

Foregoing a discussion about whether the Distance should be converted to the correct UOM by calling code, the goal here is for ToString to convert the value up or down on the same measurement (imperial or metric) that is represented by the current UnitOfMeasure.

Obviously this could all be hard coded into the ToString method, but given that I'm also implementing TypeConverters and FormatProviders for this whole shibang, I'd like to find a generic means of figuring out, from a DistanceUnit, what the appropriate next-up or next-down unit of measure would be.

Am I barking up the wrong tree by wanting to implement in this manner?

+7  A: 

Do you really need an enum here? Maybe, a simple value object will do?

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

With extension methods you and properly defined operators can get very Ruby-like syntax:

var theWholeNineYards = 9.Yards() + 34.Inches();
Anton Gogolev
Nice answer. I already have a value type for each unit of measure, as well as a Distance struct which can represent any unit of measure. My question stem from the fact that, when creating string representations (i.e. Distance.ToString("G") ), I want the string to use the most appropriate UOM that's within the same system (metric or imperial)... hence the question about hierarchies and enum. BTW - isn't adding extension methods to .NET's value types a bit of a no-no?
Mark
You can store UOM type used to create an instance of `Distance` along with `millimeters` and then use it to convert to strings. Alternatively, you can have a `MetricDistance`, `ImperialDistance`, etc. subclasses. As for a no-no, I'm talking about value _objects_ (which is a design pattern), not value _types_ (which are .NET concepts).
Anton Gogolev
+2  A: 

Generally speaking, I would go with Anton's solution. But if your implementation can't use that, and you need things to be used similar to an enum, I think this is a natural way to use the units:

DistanceUnit.Metric.Millimeter  
DistanceUnit.Imperial.Inch  

In order to use it like that, there should be:

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

Where MetricDistanceUnit is:

public enum MetricDistanceUnit  
{
   Millimeter, Centimeter ...
}

And ImperialDistanceUnit has the same structure..

Oren A
I like your suggestion, though I need a single value-type which will hold all enum values (i.e. can't split the enum into MetricDistancEUnit and ImperialDistanceUnit).
Mark
+1  A: 

Maybe all you need is a function that returns a corresponding unit subset

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;
    }
  }
}
Dialecticus
In converting the DistanceUnit back to a system (Imperial or Metric), would it be overkill to use a SortedList or SortedSet? How would you implement the reverse-conversion efficiently?
Mark
I don't run away from switches, so the answer is edited accordingly.
Dialecticus