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

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"

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

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ść.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.