2

I have the following enum:

public enum GenderContract
{
    Male,
    Female,
    Unknown
}

But I am getting from a client the values of 'M', 'F' and 'U'.

Is there a generic way to convert from the first char of the enum to the actual enum item? (Assuming that there are no collisions in names?)

I have several such scenarios and I would like to be able to have a generic way to convert partial matches to enums if possible (instead of just doing a switch statement or something like that).

Vaccano
  • 78,325
  • 149
  • 468
  • 850
  • 1
    I would do it differently. Since you need a lookup table essentially, I would use a dictionary. `Dictionary>` where the `char` would be the key to the dictionary; the M, U, F. And then the tuple would look something like, `Tuple tuple = new Tuple { {"Male", 0}, {"Unknown", 1}, {"Female", 2} }` – Ingenioushax Jul 12 '16 at 22:24

6 Answers6

5

Short solution would be to use the char values of 'M', 'F' and 'U'

public enum GenderContract
{
    Male = 77, // 'M'
    Female = 70, // 'F'
    Unknown = 80 // 'U'
};

Then you just have to do

GenderContract c1 = (GenderContract)'M';
GenderContract c2 = (GenderContract)'F';
GenderContract c3 = (GenderContract)'U';
Arthur Rey
  • 2,990
  • 3
  • 19
  • 42
  • Note that this breaks any already-serialized versions of this enum. When serialized before the change, deserializing `0` will give you `GenderContract.Male` back, but after this change you can no longer deserialize `0` because it's not in the enum any more. – Quantic Jul 12 '16 at 22:47
  • @Matthias In what way? An enum can have many members with the same value, as in `Male = 77, Monkey = 77,`. – Jeppe Stig Nielsen Jul 12 '16 at 22:54
  • @JeppeStigNielsen: damnit, must have mixed that with something.. removing.... thx! – Matthias Jul 12 '16 at 22:56
4

Instead of relying on the actual enum name you may want to consider using the DisplayAttribute. Using this means your enum value is not strongly tied to a string value. (aka someone changing Female to Woman doesn't wreck the value).

public enum GenderContract
{
  [Display(ShortName="M")]
  Male,
  [Display(ShortName="F")]
  Female,
  [Display(ShortName="U")]
  Unknown
}

Then a simple extensions methods:

public static class EnumExtensions
{
    public static TAttribute GetAttributeOrDefault<TAttribute>(this Enum enumVal)
        where TAttribute : Attribute
    {
        var type = enumVal.GetType();
        var memInfo = type.GetMember(enumVal.ToString());
        var result = memInfo[0].GetCustomAttributes(typeof(TAttribute), false)
            .FirstOrDefault() as TAttribute;

        return result;
    }

    public static string ToName(this Enum value)
    {
        var result = value.ToString();

        var attribute = value.GetAttributeOrDefault<DisplayAttribute>();

        if (attribute != null)
        {
            result = attribute.Name;
        }

        return result;
    }

    public static string ToShortName(this Enum value)
    {
        var result = value.ToString();

        var attribute = value.GetAttributeOrDefault<DisplayAttribute>();

        if (attribute != null)
        {
            result = attribute.ShortName;
        }

        return result;
    }
}

Usage:

Console.WriteLine(GenderContract.Male.ToShortName());

Result:

M

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • That looks really good! But is there an easy way to go from 'M' to GenderContract.Male? (Start with a string and go to the enum?) – Vaccano Jul 12 '16 at 22:30
  • Yes, I have written that, but it's off topic for this question. I would suggest asking a new question. Or updated your current question to say convert from enum to char and back again. – Erik Philips Jul 12 '16 at 22:31
  • My question IS that. I stated: "But I am getting from a client the values of 'M', 'F' and 'U'. Is there a generic way to convert from the first char of the enum to the actual enum item?" – Vaccano Jul 12 '16 at 22:35
  • I really like this way. Thank you for pointing me toward it. For others who get here, this answer has a way to find an enum by the short name: http://stackoverflow.com/a/38113031/16241 – Vaccano Jul 12 '16 at 22:52
  • That is an OK way. I'll post mine as it's a little more generic and easier to use. – Erik Philips Jul 13 '16 at 02:28
1

I think, a simple way would be to use a dictionary like this

var lookupTable = Enum.GetValues(typeof(GenderContract)).Cast<GenderContract>()
                  .ToDictionary(x => x.ToString()[0], x => x);

Then, It would be just a lookup.

GenderContract e = lookupTable['M'];
Eser
  • 12,346
  • 1
  • 22
  • 32
0

I would do it differently. Since you need a lookup table essentially, I would use a dictionary. Dictionary<char, tuple<string, int>> where the char would be the key to the dictionary; the M, U, F. From my comment.

And then the tuple would look something like, Tuple<string, int> tuple = new Tuple<string, int> { {"Male", 0}, {"Unknown", 1}, {"Female", 2} }. You could then access whatever you needed in the following way, myDictionaryTuple['U'].Item2 would return the integer value for Unknown = 1.

Example:

Dictionary<char, Tuple<string, int>> myDictionaryTuple = new Dictionary<char, Tuple<string, int>> { {'M', new Tuple<string, int>("Male", 0)}, {'F', new Tuple<string, int>("Female", 1) }, {'U', new Tuple<string, int>("Unknown", 2)}};

Console.WriteLine(myDictionaryTuple['U'].Item1); // Results in "Unknown"
Console.WriteLine(myDictionaryTuple['U'].Item2); // Results in 1
Console.WriteLine(myDictionaryTuple['U']); // Results in "(Unknown, 1)"
Ingenioushax
  • 718
  • 5
  • 20
0

This is the generic way I came up with to do this:

void Main()
{
    Console.WriteLine("M".SearchEnum<GenderContract>());
}

public static class EnumHelpers
{
   public static T SearchEnum<T>(this string value)
   {
       if (value == null)
          return default(T);

       // Get the string names for the Enum
       List<string> enumStrings = Enum.GetNames(typeof (T)).ToList();
       string matchingEnumString = enumStrings.FirstOrDefault(x => x.StartsWith(value));

       if (matchingEnumString == null)
          throw new ArgumentException("Cannot convert " + value + 
                                      " to enum of " + typeof(T).FullName );

       return ParseEnum<T>(matchingEnumString);

   }

   public static T ParseEnum<T>(this string value)
   {
       if (value == null)
          return default(T);

       return (T)Enum.Parse(typeof(T), value, true);
   }
}

public enum GenderContract
{
   Male,
   Female,
   Unknown
}

This enumerates the names of the enum each time it is called, so it could be more performant by storing the enum list somehow, but I don't need that much performance.

Also, if someone changes my enum values then my code will fail, but I accept that risk with this solution.

Vaccano
  • 78,325
  • 149
  • 468
  • 850
0

For English only you can map an enum to list of {enumValue, enumValue.ToString()} pairs and match on prefix of the string.

var allValues = Enum.GetValues(typeof(GenderContract))
    .Cast<GenderContract>()
    .Select(x => new {Value = x, Text = x.ToString()});
var result = allValues
   .FirstOrDefault(x => x.Text.StartsWith("F", StringComparison.OrdinalIgnoreCase));
return result != null ? result.V : default(GenderContract);

If you need more localizable solution you can map enum values to resource strings by name in similar code. I.e. have resource strings with GenderContract_Male names and find string by "enum type + enum value";

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179