Как я могу получить все константы типа по отражению?


Ответы:


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 был еще до того, как я перешел 2-ю строчку ... я заметил, что вы проходите каждый шаг с его ... намеченной целью ...! это ТАК важно , когда необходимо извлечь из него. Я бы хотел, чтобы каждый, кто имеет ваш опыт, поступил так же, как вы.
— LoneXcoder 10.12.12

4
Я не уверен насчет утверждений, касающихся IsLiteral и IsInitOnly. При тестировании может показаться, что для статических свойств только для чтения IsLiteral всегда имеет значение false, поэтому IsLiteral - единственный флаг, который необходимо проверить, чтобы найти константы, и вы можете игнорировать IsInitOnly. Я пытался с различными типами полей (например, String, Int32), чтобы увидеть, если это имеет какое-то значение, но это не так.
— Марк Уоттс

49
Также, чтобы получить значение const из FieldInfo, используйте GetRawConstantValue ().
— Сэм Сиппе

@MarkWatts прав. Может быть, поведение изменилось, так как это было опубликовано. В любом случае документация IsLiteralговорит, if 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

Почему бы (а) не сделать методы универсальными, (б) заставить методы возвращать IEnumerable<T>вместо IList?
— Вай Ха Ли

@WaiHaLee - Готово :-). Хотя очевидно, что он все еще предполагает, что все типы констант в рассматриваемом классе относятся к типу T.
— bytedev 14.12.15

2

Используйте, property.GetConstantValue()чтобы получить значение.


1
Это вполне может иметь место, когда у вас есть собственность - но как вы впервые получаете собственность?
— Вай Ха Ли

4
В .Net 4.5 это:GetRawConstantValue()
— Крис
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.