使用最佳技术创建一个键值对列表,其中键=枚举名称,值=枚举值。

3
在C# 4.0-5.0中,如何创建一个键值对列表,其中键是枚举名称的值,而值是枚举名称的值。
此外,在C#中,如何创建一个键值对列表,其中键是枚举名称,值是属性EnumName名称。
我想要根据枚举名称拥有两个不同的列表,以便从属性中检索值或可读的名称。
例如:
public enum QATypes
{

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

     }

第一个列表的值如下:

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

对于枚举成员,第二个列表将如下所示:

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

1
你能解释一下为什么需要这样做吗?第二个列表中,键应该是枚举值本身,不是吗? - svick
1
因为在我的场景中,UI变量中的字段名称总是已知的,但值并不总是已知的。 - user2943263
3个回答

4
您可以使用反射。尝试这个(假设所有枚举成员都有一个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();

进一步说,由于您的问题似乎与JSON相关,如果您想将列表序列化为JSON格式,可以使用Json.NET: Json.NET
var valuesJson = JsonConvert.SerializeObject(valuesList);
Console.WriteLine(valuesJson);

输出:

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

And

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

这将输出:

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

有关返回的顺序和异常处理的一些注释

依赖于GetEnumValuesGetEnumNames返回的项的顺序似乎不会成为潜在问题,因为这两种方法都内部依赖于相同的方法:

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

最终,这种方法是:
[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport("QCall", CharSet = CharSet.Unicode)]
private static extern void GetEnumValuesAndNames
    (RuntimeTypeHandle enumType, 
    ObjectHandleOnStack values, 
    ObjectHandleOnStack names, 
    bool getValues, 
    bool getNames);

我提供的代码片段并不涵盖所有异常情况,它依赖于原始问题中提供的示例数据。例如,它没有检查EnumMemberAttribute或其Value属性是否存在。但是这可以通过一点努力轻松改变。


1
你正在依赖于GetEnumValuesGetEnumNames返回的项目顺序,这是否有记录在案?虽然这可能有效,并且永远有效,但逻辑并不是非常合理的。问题仅针对valuesList - nawfal

3

Enum.GetValues在枚举值相同但名称不同的情况下可能存在风险,例如:

enum { A, B = A, C }

你应该使用 Enum.GetNames 方法并获取相应的值。
我有一个类似的辅助类来处理这些事情:
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();
    }
}

这是完全通用的,所以您需要更多的打字来调用。现在我可以做:

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);

您可以在合适的命名空间中创建自己的扩展方法,无需传递枚举属性类型参数,以方便调用。


同时,您还可以使辅助类非静态,帮助您更好地推断类型,从而使调用代码更短。应该如下所示:

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);

2

关于第一个问题:

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));
}

对于第二个问题:

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));
    }
}

在我看来,GetValues 对于这种情况不是很好用。当你有 e { A, B = A , C } 时,e.ToString() 将会为 B 给出 A - nawfal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接