tags:

views:

831

answers:

15

I know the following is not possible because it has to be an int

enum GroupTypes
{
    TheGroup = "OEM",
    TheOtherGroup = "CMB"
}

From my database I get a field with incomprehensive codes (the OEM and CMB's). I would want to make this field into an enum or something else understandable. Because the target is readability the solution should be terse.
What other options do I have?

+1  A: 

I would just create a dictionary and use the code as the key.

Edit: To address the comment about doing a reverse lookup (finding the key), this would not be terribly efficient. If this is necessary, I would write a new class to handle it.

jhale
Depends if it's really constant ...
C. Ross
Also can you easily grab a key for a given value?
eglasius
To C.Ross - I'm not sure what you mean. You can read the values in from a db and dynamically populate the dictionary.
jhale
+6  A: 

Create a second enum, for your DB containing the following:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

Now, you can use Enum.Parse to retrieve the correct DBGroupTypes value from the strings "OEM" and "CMB". You can then convert those to int and retrieve the correct values from the right enumeration you want to use further in your model.

Dave Van den Eynde
This seems to be an extra step in the process, why not one class that handles everything?
C. Ross
This seems much too complex for such a simple problem.
jhale
As opposed to using attributes and reflection?
Dave Van den Eynde
A: 

enums in C# are restricted to underlying integer numeric types (byte, sbyte, short, ushort, int, uint, long, and ulong). You can't associate them with a character or string based underlying value.

A different approach might be to define a dictionary of type Dictionary<string, string>.

Andrew Robinson
+10  A: 

You can add attributes to the items in the enumeration and then use reflection to get the values from the attributes.

You would have to use the "field" specifier to apply the attributes, like so:

enum GroupTypes
{
    [field:Description("OEM")]
    TheGroup,

    [field:Description("CMB")]
    TheOtherGroup
}

You would then reflect on the static fields of the type of the enum (in this case GroupTypes) and get the Description attribute for the value you were looking for.

It would also be very easy to take that discovery code and put it into a generic method which would take the enumeration value and find the attribute and return the value of the description.

casperOne
Using http://stackoverflow.com/questions/630803/enum-with-strings/630900#630900 is rather nice
Casebash
A: 

If you're trying to make your code readable:

class GroupTypes {
    public static final String (whatever oem stands for) = "OEM";
    public static final String (whatever cmb stands for) = "CMB";
    ...
}

and if you need a list of them, include these finals in a static final List<String>. This example is in Java.

If you're trying to make your application readable, add:

public static final Map<String, String> groupsByDbValue;
static {
    groupsByDbValue = new HashMap<String, String>();
    groupsByDbValue.put("OEM", "(whatever OEM stands for)");
    groupsByDbValue.put("CMB", "(whatever CMB stands for)");
}
Kevin Conner
Why would you assume Java when the question is clearly tagged C#?
Randolpho
Maybe he is more familiar with java. They are really close so everybody will understand.
borisCallens
Right you are :) I haven't written C# in quite some time and I do Java at work.
Kevin Conner
+1  A: 

Use a class.

Edit: Better example

class StarshipType
{
    private string _Name;
    private static List<StarshipType> _StarshipTypes = new List<StarshipType>();

    public static readonly StarshipType Ultralight = new StarshipType("Ultralight");
    public static readonly StarshipType Light = new StarshipType("Light");
    public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight");
    public static readonly StarshipType Heavy = new StarshipType("Heavy");
    public static readonly StarshipType Superheavy = new StarshipType("Superheavy");

    public string Name
    {
        get { return _Name; }
        private set { _Name = value; }
    }

    public static IList<StarshipType> StarshipTypes
    {
        get { return _StarshipTypes; }
    }

    private StarshipType(string name, int systemRatio)
    {
        Name = name;
        _StarshipTypes.Add(this);
    }

public static StarshipType Parse(string toParse)
{
 foreach (StarshipType s in this.StarshipTypes)
 {
  if (toParse == s.Name)
   return s;
 }
 throw new FormatException("Could not parse string.");
}
}
C. Ross
Difficult to go from the code back to the descriptive name. You would have to use reflection over all the const fields to search for a match.
Andrew Robinson
I see your point. I will upload a version that acutally works later, but I admit it's pretty heavy.
C. Ross
A: 

Try adding constants to a static class. You do not end up with a Type, but you have readable, organised constants:

public static class GroupTypes
{
    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB"
}
darasd
Difficult to go from the code back to the descriptive name. You would have to use reflection over all the const fields to search for a match.
Andrew Robinson
A: 

Have you considered a lookup table using a Dictionary?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);

You can then use GroupTypeLookup.TryGetValue() to look up a string when you read it.

Jim Mischel
How do you easily get the key for a given value?
eglasius
The question didn't ask to go the other way. But it'd be simple enough to build another dictionary that goes the other way. That is, Dictionary<GroupTypes, string>.
Jim Mischel
+1  A: 

I would make it into a class an avoid an enum altogether. And then with the usage of a typehandler you could create the object when you grab it from the db.

IE:

public class Group
{
    public string Value{ get; set; }
    public Group( string value ){ Value = value; } 
    public static Group TheGroup() { return new Group("OEM"); }
    public static Group OtherGroup() { return new Group("CMB"); }

}
Bryan Rowe
+7  A: 

You could also use the extension model:

public enum MyEnum
{
    [Description("String 1")]
    V1= 1,
    [Description("String 2")]
    V2= 2
}

Your Extension Class

public static class MyEnumExtensions
{
    public static string ToDescriptionString(this MyEnum val)
    {
        DescriptionAttribute[] attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : string.Empty;
    }
}

usage:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());
Glennular
A: 

My first question - Do you have access to the Database itself? This should be normalized in the database, ideally, otherwise, any solution is going to be prone to error. In my experience, data fields full of "OEM" and "CMB" tend to wind up having things like "oem " and other 'crap data' mixed in over time.... If you can normalize it, you could use the key in the table containing the elements as your Enum, and you're done, with a much cleaner structure.

If that's not available, I'd make your Enum, and make a class to parse your string into the Enum for you. This would at least give you some flexibility in handling non-standard entries and much more flexibility for trapping or handling errors than doing any of the workarounds using Enum.Parse/Reflection/etc. A dictionary would work, but could break down if you ever have case issues, etc.

I'd recommend writing a class so you can do:

// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);

This preserves most of your readability without having to change the DB.

Reed Copsey
+11  A: 

I like to use properties in a class instead of methods, since they look more enum-like.

Here's a example for a Logger:

public class LogCategory
{
 private LogCategory(string value) { Value = value; }

 public string Value { get; set; }

 public static LogCategory Trace { get { return new LogCategory("Trace"); } }
 public static LogCategory Debug { get { return new LogCategory("Debug"); } }
 public static LogCategory Info { get { return new LogCategory("Info"); } }
 public static LogCategory Warning { get { return new LogCategory("Warning"); } }
 public static LogCategory Error { get { return new LogCategory("Error"); } }
}

Pass in type-safe string values as a parameter:

public static void Write(string message, LogCategory logCategory)
{
   var log = new LogEntry { Message = message };
   Logger.Write(log, logCategory.Value);
}

Usage:

Logger.Write("This is almost like an enum.", LogCategory.Info);
Even Mien
Only down side I can come up with is that it would be a tiny bit slower, but this would in most cases be neglectable. And it wouldn't have the exact same behaviour in the editor. E.G.: switching over this one wouldn't automatically fill in a case for each possibility. Other than those minor points, I think this is probably a rather simple solution.
borisCallens
And it's easy to use Dictionary<LogCategory, Action/Func> as a switch. :)
Arnis L.
interesting hack :)
dr. evil
+1  A: 

You can do it very easily actually. Use the following code.

enum GroupTypes
{
   OEM,
   CMB
};

Then when you want to get the string value of each enum element just use the following line of code.

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);

I've used this method successfully in the past, and I've also used a constants class to hold string constants, both work out pretty well, but I tend to prefer this.

Arthur C
I was thinking the same thing, but there must be some catch to this... Otherwise I would suspect more people would suggest this (Maybe I'm just paranoid).
Matthijs Wessels
The only catch I'm aware of to this is that I believe it uses reflection to figure out the string. As a result if I'm just after a solution to keep track of a constant string, then I typically will use a class to store the majority of my constant strings.However if I have situation where an Enum is the right solution (regardless of getting a descriptive string about my Enum elements), then rather than have an extra string floating around somewhere to manage I just use the enum value as described.
Arthur C
+1  A: 

Answer by Even:

public class LogCategory
{
 private LogCategory(string value) { Value = value; }

 public string Value { get; set; }

 public static LogCategory Trace { get { return new LogCategory("Trace"); } }
 public static LogCategory Debug { get { return new LogCategory("Debug"); } }
 public static LogCategory Info { get { return new LogCategory("Info"); } }
 public static LogCategory Warning { get { return new LogCategory("Warning"); } }
 public static LogCategory Error { get { return new LogCategory("Error"); } }
}


Just wanted to add a way how to mimic switch with class based enums:

public void Foo(LogCategory logCategory){    

  var @switch = new Dictionary<LogCategory, Action>{
    {LogCategory.Trace, ()=>Console.Writeline("Trace selected!")},
    {LogCategory.Debug, ()=>Console.Writeline("Debug selected!")},
    {LogCategory.Error, ()=>Console.Writeline("Error selected!")}};

   //will print one of the line based on passed argument
  @switch[logCategory]();
}
Arnis L.
A: 

Here is the extension method that I used to get the enum value as string. First here is the enum.

 public enum DatabaseEnvironment
        {
            [Description("AzamSharpBlogDevDatabase")]
             Development = 1, 
            [Description("AzamSharpBlogQADatabase")]
             QualityAssurance = 2, 
            [Description("AzamSharpBlogTestDatabase")] 
            Test = 3
        }

The Description attribute came from System.ComponentModel.

And here is my extension method:

public static string GetValueAsString(this DatabaseEnvironment environment) { // get the field var field = environment.GetType().GetField(environment.ToString());

        var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false);

        if(customAttributes.Length > 0)
        {
            return (customAttributes[0] as DescriptionAttribute).Description;  
        }
        else
        {
           return environment.ToString(); 
        }
    }

Now, you can access the enum as string value using the following code:

 [TestFixture]
    public class when_getting_value_of_enum
    {
        [Test]
        public void should_get_the_value_as_string()
        {
            Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
        }
    }
azamsharp