如何通过反射获取类型的所有常量?


Answers:


264

虽然这是一个旧代码:

private FieldInfo[] GetConstants(System.Type type)
{
    ArrayList constants = new ArrayList();

    FieldInfo[] fieldInfos = type.GetFields(
        // Gets all public and static fields

        BindingFlags.Public | BindingFlags.Static | 
        // This tells it to get the fields from all base types as well

        BindingFlags.FlattenHierarchy);

    // Go through the list and only pick out the constants
    foreach(FieldInfo fi in fieldInfos)
        // IsLiteral determines if its value is written at 
        //   compile time and not changeable
        // IsInitOnly determines if the field can be set 
        //   in the body of the constructor
        // for C# a field which is readonly keyword would have both true 
        //   but a const field would have only IsLiteral equal to true
        if(fi.IsLiteral && !fi.IsInitOnly)
            constants.Add(fi);           

    // Return an array of FieldInfos
    return (FieldInfo[])constants.ToArray(typeof(FieldInfo));
}

资源

您可以使用泛型和LINQ轻松将其转换为更清晰的代码:

private List<FieldInfo> GetConstants(Type type)
{
    FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Public |
         BindingFlags.Static | BindingFlags.FlattenHierarchy);

    return fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
}

或一行:

type.GetFields(BindingFlags.Public | BindingFlags.Static |
               BindingFlags.FlattenHierarchy)
    .Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();

13
我的+1是在我甚至没有通过第二条线之前的。这是SO当需要从中吸取教训非常重要的。我希望每个有您经验的人都能像您在这里做的那样。
LoneXcoder 2012年

4
我不确定与IsLiteral和IsInitOnly有关的断言。在测试中,对于静态只读属性,似乎IsLiteral始终为false-因此IsLiteral是您需要检查以查找常量的唯一标志,并且可以忽略IsInitOnly。我尝试了不同的字段类型(例如String,Int32),看是否有什么不同,但没有。
马克·瓦茨

49
另外,要从FieldInfo获取const的值,请使用GetRawConstantValue()。
Sam Sippe

@MarkWatts是正确的。自发布以来,行为可能已更改。在任何情况下,文件IsLiteralif its value is written at compile time,只有常量是真实的,这是怎么回事,现在表现(如测试.NET 4.5.2)
nawfal

52

如果要从目标类型获取特定类型的所有常量的,请使用以下扩展方法(扩展此页面上的一些答案):

public static class TypeUtilities
{
    public static List<T> GetAllPublicConstantValues<T>(this Type type)
    {
        return type
            .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
            .Where(fi => fi.IsLiteral && !fi.IsInitOnly && fi.FieldType == typeof(T))
            .Select(x => (T)x.GetRawConstantValue())
            .ToList();
    }
}

然后这样的课

static class MyFruitKeys
{
    public const string Apple = "apple";
    public const string Plum = "plum";
    public const string Peach = "peach";
    public const int WillNotBeIncluded = -1;
}

您可以string像这样获得常量值:

List<string> result = typeof(MyFruitKeys).GetAllPublicConstantValues<string>();
//result[0] == "apple"
//result[1] == "plum"
//result[2] == "peach"

为什么不这样.Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();呢?
T-moty

17

作为类型扩展名:

public static class TypeExtensions
{
    public static IEnumerable<FieldInfo> GetConstants(this Type type)
    {
        var fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);

        return fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly);
    }

    public static IEnumerable<T> GetConstantsValues<T>(this Type type) where T : class
    {
        var fieldInfos = GetConstants(type);

        return fieldInfos.Select(fi => fi.GetRawConstantValue() as T);
    }
}

1
显然,这是如果类型上的常量都是字符串;-)
bytedev

为什么不(a)使方法通用,(b)使方法返回IEnumerable<T>而不是IList
李慧夏

@WaiHaLee-完成:-)。尽管显然它仍然假定所讨论类上的所有
const

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.