Come posso ottenere tutte le costanti di un tipo per riflessione?


Risposte:


264

Sebbene sia un vecchio codice:

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

fonte

Puoi facilmente convertirlo in codice più pulito usando generics e 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();
}

O con una riga:

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

13
Il mio +1 era ancora prima di aver superato la seconda linea ... ho notato che stai attraversando ogni fase con il suo ... scopo designato ...! questo è COSÌ importante quando si deve imparare da esso. vorrei che ognuno con la tua esperienza facesse come hai fatto qui.
LoneXcoder il

4
Non sono sicuro delle affermazioni relative a IsLiteral e IsInitOnly. Al momento del test sembra che IsLiteral per le proprietà statiche di sola lettura sia sempre falso, quindi IsLiteral è l'unico flag che devi controllare per trovare le costanti e puoi ignorare IsInitOnly. Ho provato con diversi tipi di campo (ad esempio String, Int32) per vedere se questo ha fatto la differenza, ma non è stato così.
Mark Watts,

49
Inoltre, per ottenere il valore della const da FieldInfo, utilizzare GetRawConstantValue ().
Sam Sippe,

@MarkWatts ha ragione. Potrebbe essere cambiato il comportamento da quando è stato pubblicato. In ogni caso la documentazione di IsLiteraldice if its value is written at compile timee questo è vero solo per le costanti, che è come si comporta ora (testato a partire da .NET 4.5.2)
nawfal

52

Se desideri ottenere i valori di tutte le costanti di un tipo specifico, dal tipo di destinazione, ecco un metodo di estensione (che estende alcune delle risposte in questa pagina):

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

Quindi per una lezione come questa

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

È possibile ottenere i stringvalori costanti in questo modo:

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

Perché non questo .Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();:?
T-moty,

17

Come estensioni di tipo:

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
Ovviamente questo è se le tue costanti su un tipo sono tutte stringhe ;-)
bytedev l'

Perché non (a) rendere generici i metodi, (b) rendere i metodi restituiti IEnumerable<T>anziché un IList?
Wai Ha Lee,

@WaiHaLee - Fatto :-). Anche se ovviamente si presume che tutti i tipi di salti sulla classe in questione siano di tipo T.
bytedev

2

Utilizzare property.GetConstantValue()per ottenere valore.


1
Questo potrebbe essere il caso quando si possiede la proprietà, ma come si ottiene per la prima volta la proprietà?
Wai Ha Lee,

4
In .Net 4.5 è:GetRawConstantValue()
Chris
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.