Jak mogę uzyskać wszystkie stałe typu przez odbicie?

Odpowiedzi:

264

Chociaż to stary kod:

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

Źródło

Możesz łatwo przekonwertować go na czystszy kod za pomocą typów ogólnych i 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();
}

Lub jedną linią:

type.GetFields(BindingFlags.Public | BindingFlags.Static |
               BindingFlags.FlattenHierarchy)
    .Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
gdoron wspiera Monikę
źródło
13
Moje +1 było, zanim jeszcze przekroczyłem drugą linię ... Zauważyłem, że przechodzisz przez każdy krok z jego ... zamierzonym celem ...! to jest tak ważne, gdy trzeba uczyć się od niego. Chciałbym, żeby każdy z twoim doświadczeniem zrobił to samo, co tutaj.
LoneXcoder,
4
Nie jestem pewien co do stwierdzeń dotyczących IsLiteral i IsInitOnly. Podczas testowania wydawałoby się, że dla statycznych właściwości tylko do odczytu IsLiteral jest zawsze fałszywa - więc IsLiteral jest jedyną flagą, którą musisz sprawdzić, aby znaleźć stałe, i możesz zignorować IsInitOnly. Próbowałem z różnymi typami pól (np. String, Int32), aby sprawdzić, czy to coś zmienia, ale tak się nie stało.
Mark Watts
49
Ponadto, aby uzyskać wartość stałej z FieldInfo, użyj GetRawConstantValue ().
Sam Sippe,
@MarkWatts ma rację. Może to zmienić zachowanie od czasu opublikowania tego. W każdym razie dokumentacja IsLiteralmówi if its value is written at compile timei jest prawdziwa tylko dla stałych, tak to zachowuje się teraz (testowane od .NET 4.5.2)
nawfal
52

Jeśli chcesz uzyskać wartości wszystkich stałych określonego typu, z typu docelowego, oto metoda rozszerzenia (rozszerzająca niektóre odpowiedzi na tej stronie):

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

Następnie dla takiej klasy

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

Możesz otrzymać stringstałe wartości w następujący sposób:

List<string> result = typeof(MyFruitKeys).GetAllPublicConstantValues<string>();
//result[0] == "apple"
//result[1] == "plum"
//result[2] == "peach"
BCA
źródło
Dlaczego nie to .Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();:?
T-moty
17

Jako rozszerzenia typu:

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
źródło
1
Oczywiście
dzieje się
Dlaczego nie (a) uczynić metod ogólnymi, (b) sprawić, by metody zwracały IEnumerable<T>zamiast an IList?
Wai Ha Lee
@WaiHaLee - Gotowe :-). Choć oczywiście nadal zakłada, że ​​wszystkie typy stałych w danej klasie są typu T.
bytedev
2

Użyj, property.GetConstantValue()aby uzyskać wartość.

Reza Bayat
źródło
1
Może tak być w przypadku posiadania nieruchomości - ale jak ją zdobyć?
Wai Ha Lee
4
W .Net 4.5 jest:GetRawConstantValue()
Chris