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

Ответы:

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"
BCA
источник
Почему не это .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);
    }
}
bytedev
источник
1
Очевидно, это если ваши константы в типе все строки ;-)
bytedev
Почему бы (а) не сделать методы универсальными, (б) заставить методы возвращать IEnumerable<T>вместо IList?
Вай Ха Ли
@WaiHaLee - Готово :-). Хотя очевидно, что он все еще предполагает, что все типы констант в рассматриваемом классе относятся к типу T.
bytedev 14.12.15
2

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

Реза Баят
источник
1
Это вполне может иметь место, когда у вас есть собственность - но как вы впервые получаете собственность?
Вай Ха Ли
4
В .Net 4.5 это:GetRawConstantValue()
Крис