user2943263
user2943263

Reputation: 53

Create a list of Key Value pairs using Key = Enums Name and Value = Enums value using best technique

In C# 4.0 - 5.0 how do you create a list of key value pairs where the key is the enums name's value and the value is the enums name value.

Also in C# how do you create a list of key value pairs where the key is the enums name and the value is the attributes EnumName name.

I want to have two seperate list based on the enum's name in order to retrieve the value or the human readable name from the attribtue.

example:

public enum QATypes
{

    [EnumMember(Value = "Black Box")]
     BlackBox = 10,
    [EnumMember(Value = "Integration Tests")]
     IntegrationTests = 20,
    [EnumMember(Value = "Acceptance Testing")]
     AcceptanceTesting= 30,
...

     }

and the first list would look like this for values:

{{ key:"BlackBox", value:10 }, { key:"IntegrationTests ", value:20 },{ key:"AcceptanceTesting", value:30 },...}

and the second list would look like this for enumsmember:

{{ key:"BlackBox", value:"Black Box"}, { key:"IntegrationTests ", value:"Integration Tests"},{ key:"AcceptanceTesting", value:"Acceptance Testing"},...}

Upvotes: 3

Views: 11762

Answers (3)

nawfal
nawfal

Reputation: 73173

Enum.GetValues is dangerous when you have enum members with same values but different names, like:

enum { A, B = A, C }

You should use Enum.GetNames and then get the corresponding values.

I have this helper class to deal with similar things:

public static class Enum<T> where T : struct, IComparable, IFormattable, IConvertible
{
    public static IEnumerable<T> GetValues()
    {
        return (T[])Enum.GetValues(typeof(T));
    }

    public static IEnumerable<string> GetNames()
    {
        return Enum.GetNames(typeof(T));
    }

    public static T Parse(string @enum)
    {
        return (T)Enum.Parse(typeof(T), @enum);
    }

    public static S GetAttribute<S>(string @enum) where S : Attribute
    {
        return (S)typeof(T).GetMember(@enum)[0]
                           .GetCustomAttributes(typeof(S), false)
                           .SingleOrDefault();
    }
}

This is fully generic, so you need a bit more typing to call. Now I can do:

var first = Enum<QATypes>.GetNames().ToDictionary(x => x, x => (int)Enum<QATypes>.Parse(x));

var second = first.ToDictionary(x => x.Key, x => Enum<QATypes>.GetAttribute<EnumMemberAttribute>(x.Key).Value);

You can create your own extension method in a suitable namespace where you need not pass the enum attribute type argument, to ease calling.


Also you can make the helper class non-static which will help you with type-inference better and thus make calling code shorter. Should look like:

var helper = new Enum<QATypes>();

var first = helper.GetNames().ToDictionary(x => x, x => (int)helper.Parse(x));

var second = first.ToDictionary(x => x.Key, x => helper.GetAttribute<EnumMemberAttribute>(x.Key).Value);

Upvotes: 3

Alex Filipovici
Alex Filipovici

Reputation: 32561

You may use Reflection. Try this (assuming that all the enum members have an EnumMemberAttribute):

var qatype = typeof(QATypes);
var names = qatype.GetEnumNames();
var values = qatype.GetEnumValues().Cast<int>().ToList();
var nameValues = names.Select(n =>
        qatype.GetMember(n)[0]
            .CustomAttributes.First()
            .NamedArguments[0].TypedValue
            .Value)
    .ToList();
var valuesList = names.Select((n, index) =>
        new { key = n, value = values[index] })
    .ToList();
var nameValuesList = names.Select((n, index) =>
        new { key = n, value = nameValues[index] })
    .ToList();

Further on, since your question seems to be JSON - related, if you want to serialize the lists in the JSON format, you may use Json.NET:

var valuesJson = JsonConvert.SerializeObject(valuesList);
Console.WriteLine(valuesJson);

Output:

[
    {
        "key": "BlackBox",
        "value": 10
    },
    {
        "key": "IntegrationTests",
        "value": 20
    },
    {
        "key": "AcceptanceTesting",
        "value": 30
    }
]

And

var nameValuesJson = JsonConvert.SerializeObject(nameValuesList);
Console.WriteLine(nameValuesJson);

Which outputs:

[
    {
        "key": "BlackBox",
        "value": "Black Box"
    },
    {
        "key": "IntegrationTests",
        "value": "Integration Tests"
    },
    {
        "key": "AcceptanceTesting",
        "value": "Acceptance Testing"
    }
]

Some remarks about the returned order and exception handling

Relying on the order of the items returned by the GetEnumValues and GetEnumNames seems not to be a potential issue, as both these methods rely internally on the same method:

[SecuritySafeCritical]
private static void GetCachedValuesAndNames
    (RuntimeType enumType, 
    out ulong[] values, 
    out string[] names, 
    bool getValues, 
    bool getNames)

and ultimately on this method:

[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport("QCall", CharSet = CharSet.Unicode)]
private static extern void GetEnumValuesAndNames
    (RuntimeTypeHandle enumType, 
    ObjectHandleOnStack values, 
    ObjectHandleOnStack names, 
    bool getValues, 
    bool getNames);

The code snippet I provided doesn't cover all exceptions, it relies on the sample data provided in the original question. For example, it doesn't check for the existence of the EnumMemberAttribute or it's Value property. But this can be easily changed with a little amount of effort.

Upvotes: 4

Alberto
Alberto

Reputation: 15941

For the first question:

var list = new List<KeyValuePair<string,int>>();

foreach(var e in Enum.GetValues(typeof(QATypes)))
{
    list.Add(new KeyValuePair<string,int>(e.ToString(), (int)e));
}

for the second question:

var list = new List<KeyValuePair<string,string>>();
foreach(var e in typeof(QATypes).GetFields())
{
    var attribute = e.GetCustomAttributes(typeof(EnumMemberAttribute))
                             .FirstOrDefault() as EnumMemberAttribute;
    if(attribute != null)
    {
        list.Add(new KeyValuePair<string,string>(e.Name, attribute.Value));
    }
}

Upvotes: 2

Related Questions