Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use Enum, static Class, Dictionary or Struct to represent these "labeled floats" in C#?

I have a constant data structure that represents the relative height of each human vertebra, normalized in relation to total spine height. This is derived from anthropometric studies, etc.

I have implemented it in Python as a tuple of tuples, each tuple containing a (string)Name and (double)Value, like this:

vertebral_heights = (
("C7",  0.0000000),
("T1",  0.0391914),
("T2",  0.0785479),
("T3",  0.1183993),
("T4",  0.1590759),
("T5",  0.2009076),
("T6",  0.2442244),
("T7",  0.2893564),
("T8",  0.3366337),
("T9",  0.3863861),
("T10", 0.4389439),
("T11", 0.4946370),
("T12", 0.5537954),
("L1",  0.6167492),
("L2",  0.6838284),
("L3",  0.7553630),
("L4",  0.8316832),
("L5",  0.9131188),
("S1",  1.0000000))

My first thought was to create a Dictionary, but that would need a class to be used as a container. Then the idea of an Enum came to mind, but I have read "enums are for ints", and I have doubles. Then there are Class and Struct, but to this point I am utterly confused, and I believe my current understanding of the best practices of doing this stuff in C# is not enough, yet.

My intended use is to have a "map" between the application model (the numeric part of the elements) and the user model (the named, domain-related part of the elements).

Any suggestion?

like image 562
heltonbiker Avatar asked Apr 04 '13 13:04

heltonbiker


4 Answers

It really depends on how you want to access the values.

Constants

If you will always use variable names, for example:

double x = C7;

then you can just use a class full of constants like so:

public class VertebralHeights
{
    public const double C7 = 0.0000000d;
}

Dictionary

However, if you want to access them dynamically, for example:

string id = "C7";
double x = VertebralHeights[id];

then you will be better off with a Dictionary which you can define like so:

Dictionary<string, double> VertebralHeights = new Dictionary<string, double>()
{
    { "C7", 0.0000000d },
    { "T1", 0.0391914d}
}

Having both ways together.

If you want both strongly-typed and dynamic access to the value you can extend either of the above methods...

For constants (method 1) add a function that takes a string:

public double GetValue(string s)
{
    switch(s)
    {
        case "C7": return C7;
        case "T7": return T7;
        //...and so on...
        default: return 0;//or an alternate default
    }
}

(note: you could do this with reflection instead, which would be easier with a massive list, but isn't really worth the extra performance hit here)

For the Dictionary approach (method 2), you could add a collection of getters:

public double C7 { get { return VertebralHeights["C7"]; } }
like image 144
musefan Avatar answered Nov 04 '22 17:11

musefan


Here's my take on this - work with a singleton class which is a Dictionary:

public class Vertebrae : Dictionary<string, double>
{
    private Vertebrae() : base() { }


    private static Vertebrae _heights = new Vertebrae() {
        { "C7", 0.0 },
        { "T1", 0.0391914 },
        { "T2", 0.0785479 },
    };

    public static Vertebrae Heights { get { return _heights; } }

    public static double C7 { get { return Heights["C7"]; } }
    public static double T1 { get { return Heights["T1"]; } }
    public static double T2 { get { return Heights["T2"]; } }

    public static IEnumerable<double> All
    {
        get
        {
            return new List<double>() { C7, T1, T2 };
        }
    }
}

To access your Vertebrae by string name, you do:

double c7 = Vertebrae.Heights["C7"];

To access your Vertebrae by symbolic name, you do:

double c7 = Vertebrae.C7;

To enumerate your Vertebrae you do:

foreach (double v in Vertebrae.All) { /* ... */ }

For the enumerator you could have a single static List initialized as in the enumerator, but I wasn't sure which would get initialized first, the static list or the static dictionary...

like image 7
plinth Avatar answered Nov 04 '22 18:11

plinth


Do it as an enum, and write the black-box plumbing code up front. You will not regret it! Here's what I would do:

Write a custom attribute so that you can associate the double value to each enum:

[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
internal sealed class VertebralHeightAsDoubleAttribute : Attribute
{
  public double HeightValue { get; private set; }

  public VertebralHeightAsDoubleAttribute(double heightValue_)
  {
    HeightValue = heightValue_;
  }
}   

Some extension methods to make life easier:

public static class VHAttribExtensions
{
  public static string ToNameString(this VertebralHeight target)
  {
    return Enum.GetName(typeof(VertebralHeight), target);
  }

  public static double ToHeightValue(this VertebralHeight target)
  {
    var fi = target.GetType().GetField(target.ToString());
    var attributes = (VertebralHeightAsDoubleAttribute[])fi.GetCustomAttributes(
      typeof(VertebralHeightAsDoubleAttribute), false);
    return attributes.Length > 0 ? attributes[0].HeightValue : double.NaN;
  }
}

Define your enum using the custom attribute:

public enum VertebralHeight
{
  [VertebralHeightAsDouble(0.0000000)]
  C7,
  [VertebralHeightAsDouble(0.0391914)]
  T1,
  [VertebralHeightAsDouble(0.0785479)]
  T2,
  [VertebralHeightAsDouble(0.1183993)]
  T3,
  [VertebralHeightAsDouble(0.1590759)]
  T4,
  [VertebralHeightAsDouble(0.2009076)]
  T5,
  [VertebralHeightAsDouble(0.2442244)]
  T6,
  [VertebralHeightAsDouble(0.2893564)]
  T7,
  [VertebralHeightAsDouble(0.3366337)]
  T8,
  [VertebralHeightAsDouble(0.3863861)]
  T9,
  [VertebralHeightAsDouble(0.4389439)]
  T10,
  [VertebralHeightAsDouble(0.4946370)]
  T11,
  [VertebralHeightAsDouble(0.5537954)]
  T12,
  [VertebralHeightAsDouble(0.6167492)]
  L1,
  [VertebralHeightAsDouble(0.6838284)]
  L2,
  [VertebralHeightAsDouble(0.7553630)]
  L3,
  [VertebralHeightAsDouble(0.8316832)]
  L4,
  [VertebralHeightAsDouble(0.9131188)]
  L5,
  [VertebralHeightAsDouble(1.0000000)]
  S1
}

Test it:

static void Main(string[] args)
{
  var list = Enum.GetValues(typeof(VertebralHeight)).OfType<VertebralHeight>();
  foreach (var vh in list)
  {
    Console.WriteLine("{0} : {1}", vh.ToNameString(), vh.ToHeightValue());
  }
  Console.ReadLine();
}
like image 6
code4life Avatar answered Nov 04 '22 17:11

code4life


You could create a class:

public static class VertebralHeights
{
    public const double C7 = 0.0000000;
    public const double T1 = 0.0391914;
    //...
}

Access: double c7 = VertebralHeights.C7;

like image 4
pascalhein Avatar answered Nov 04 '22 17:11

pascalhein