1

Not able to parse into enum object from its StringValue attribute.

enum:

public enum StatusColor
{
    [StringValue("#FFFFFF")]
    None,

    [StringValue("#5DB516")]
    Green,

    [StringValue("#F3212A")]
    Red,

    [StringValue("#FFFFFF")]
    White
}

parsing try 1

string inputHtmlColor = "#F3212A"; // input
StatusColor outColor; // output
Enum.TryParse(inputHtmlColor , true, out outColor);

parsing try 2:

string inputHtmlColor = "#F3212A"; //input
StatusColor outColor = Enum.Parse(typeof(StatusColor), inputHtmlColor, true);

Both code is not working, codes always selecting StausColor.None (the first one). How can I get the right StatusColor enum object?

Prem
  • 316
  • 1
  • 5
  • 23
  • 1
    Let me know if the linked question doesn't answer yours (by tagging me in a comment `@john`) and I'll reopen your question. – ProgrammingLlama Jun 04 '19 at 04:33
  • 1
    Hi @John, the answer you suggested is opposite of what I want. The answer is about from object to its attribute value while I already have attribute value and I and to convert it into enum object. – Prem Jun 04 '19 at 05:06
  • Please reopen my question. – Prem Jun 04 '19 at 05:06
  • Ah, understood. My bad! – ProgrammingLlama Jun 04 '19 at 05:08
  • One question. What is the meaning of the square bracket here? – kame Jun 04 '19 at 05:28
  • 1
    @kame `[ ]` denotes an [attribute](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/). – ProgrammingLlama Jun 04 '19 at 05:30
  • 1
    Hi @kame, ```[ ]``` is similar like ```@``` annotation in Java, if you are from Java background. – Prem Jun 04 '19 at 05:38
  • 1
    I literally just did this today. Search for “convert to enum from description attribute” and change the code to use that attribute. – theMayer Jun 04 '19 at 05:40
  • Possible duplicate of [Converting enum to string (and viceversa) using annotations](https://stackoverflow.com/questions/32189481/converting-enum-to-string-and-viceversa-using-annotations) – theMayer Jun 04 '19 at 05:45
  • 1
    @δev: The relatively tricky part of the code is finding all the attribute values in the enum. It should be reasonably straightforward to change that code to go the other way round. Have you tried to do so? If so, how far did you get, and what happened? – Jon Skeet Jun 04 '19 at 06:18

5 Answers5

2

This should do it:

public StatusColor GetColor(string color)
{
    return
        Enum.GetValues(typeof(StatusColor))
            .Cast<StatusColor>()
            .First(x => ((StringValueAttribute)typeof(StatusColor)
                        .GetField(x.ToString())
                        .GetCustomAttribute(typeof(StringValueAttribute))).Value == color);
}
Magnus
  • 45,362
  • 8
  • 80
  • 118
1

I would create a reverse lookup dictionary that takes the enum value and returns a matching enum value:

public static IDictionary<TKey, TEnum> GetReverseEnumLookup<TEnum, TKey, TAttribute>(Func<TAttribute, TKey> selector, IEqualityComparer<TKey> comparer = null)
    where TEnum: struct, IConvertible // pre-C#7.3
    // where TEnum : System.Enum // C#7.3+
    where TAttribute: System.Attribute
{
    // use the default comparer for the dictionary if none is specified
    comparer = comparer ?? EqualityComparer<TKey>.Default;

    // construct a lookup dictionary with the supplied comparer
    Dictionary<TKey, TEnum> values = new Dictionary<TKey, TEnum>(comparer);

    // get all of the enum values
    Type enumType = typeof(TEnum);
    var enumValues = typeof(TEnum).GetEnumValues().OfType<TEnum>();

    // for each enum value, get the corresponding field member from the enum
    foreach (var val in enumValues)
    {
        var member = enumType.GetMember(val.ToString()).First();

        // if there is an attribute, save the selected value and corresponding enum value in the dictionary
        var attr = member.GetCustomAttribute<TAttribute>();
        if (attr != null) 
        {
            values[selector(attr)] = val;
        }
    }
    return values;
}

I've made this method as generic as possible so that it can apply to many use cases. Usage in your case would look like this:

var lookup = GetReverseEnumLookup<StatusColor, string, StringValueAttribute>(v => v.Value, StringComparer.OrdinalIgnoreCase); // I figure you want this to be case insensitive

And then you can store the lookup statically somewhere, and lookup values like this:

StatusColor color;
if (lookup.TryGetValue("#ffffff", out color))
{
    Console.WriteLine(color.ToString());
}
else
{
    // not found
}

Try it online

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
  • Hi @John, not an straight answer but I really liked the idea to have relationship Dictionary and look inside it. May be I'll create my own and for case insensitivity I'll look by converting the key case to same I have stored. Thanks.! – Prem Jun 04 '19 at 05:46
  • @δev I just figured you were likely to need some degree of performance (which a dictionary will provide). I dislike the alternative methods I've seen for this since they often evaluate the entire enum using reflection every single time you need to convert a value. It works, but it's not very efficient. – ProgrammingLlama Jun 04 '19 at 05:49
1

Your calling code:

string inputHtmlColor = "#F3212A";
StatusColor outColor = inputHtmlColor.GetEnumFromString<StatusColor>();

That's how you would call the below extension method.

  • I used and modified the below extension method which came from here
public static class EnumEx
{
    public static T GetEnumFromString<T>(this string stringValue)
    {
        var type = typeof(T);
        if (!type.IsEnum) throw new InvalidOperationException();
        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(StringValueAttribute)) as StringValueAttribute;
            if (attribute != null)
            {
                if (attribute.Value == stringValue)
                    return (T)field.GetValue(null);
            }
            else
            {
                if (field.Name == stringValue)
                    return (T)field.GetValue(null);
            }
        }
        throw new ArgumentException("Not found.", "stringValue");
        // or return default(T);
    }
}

note on the Attribute:

I defined my attribute like this:

public class StringValueAttribute : Attribute
{
    public string Value { get; private set; }

    public StringValueAttribute(string value)
    {
        Value = value;
    }
}

If your StringValueAttribute is defined differently, feel free to update your question to include that type definition, and I'll update my answer if necessary.

egnomerator
  • 985
  • 8
  • 15
0

I have created one method for that as below

StringEnum.GetStringValue(Pass Your enum here)

Call this function which is created in common class

public static string GetStringValue(Enum value)
        {
            string output = null;
            try
            {
                Type type = value.GetType();

                if (_stringValues.ContainsKey(value))
                    output = (_stringValues[value] as StringValueAttribute).Value;
                else
                {
                    ////Look for our 'StringValueAttribute' in the field's custom attributes
                    FieldInfo fi = type.GetField(value.ToString());
                    StringValueAttribute[] attrs = fi.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
                    if (attrs.Length > 0)
                    {
                        _stringValues.Add(value, attrs[0]);
                        output = attrs[0].Value;
                    }

                }
            }
            catch (Exception)
            {

            }

            return output;

        }
rvchauhan
  • 89
  • 8
  • Hi @rvchauhan, this is not what I'm looking for. I already have GetStringValue method to convert from object to string attrb value. I want just opposite of it. Want from String attrb value to enum object. Thanks.! – Prem Jun 04 '19 at 05:34
0

This what working with minimal change:

Util class:

private static IDictionary<string, StatusColor> _statusColorByHtml = Enum.GetValues(typeof(StatusColor)).Cast<StatusColor>().ToDictionary(k => k.GetStringValue(), v => v);
public static StatusColor GetStatusColor(string htmlColor)
{
    _statusColorByHtml.TryGetValue(htmlColor, out StatusColor color);
    return color;
}
Prem
  • 316
  • 1
  • 5
  • 23