Comment puis-je obtenir toutes les constantes d'un type par réflexion?

Réponses:

264

Bien que ce soit un ancien code:

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

La source

Vous pouvez facilement le convertir en code plus propre à l'aide de génériques et de 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();
}

Ou avec une ligne:

type.GetFields(BindingFlags.Public | BindingFlags.Static |
               BindingFlags.FlattenHierarchy)
    .Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
gdoron soutient Monica
la source
13
Mon +1 était avant même d'avoir franchi la deuxième ligne ... j'ai remarqué que vous traversiez chaque étape avec son ... objectif prévu par la conception ...! c'est SI important quand on a besoin d'en tirer des leçons. Je souhaite que chacun avec votre expérience fasse comme vous l'avez fait ici.
LoneXcoder
4
Je ne suis pas sûr des affirmations concernant IsLiteral et IsInitOnly. Lors des tests, il semblerait que pour les propriétés statiques en lecture seule, IsLiteral soit toujours faux - donc IsLiteral est le seul indicateur que vous devez vérifier pour trouver des constantes et vous pouvez ignorer IsInitOnly. J'ai essayé avec différents types de champs (par exemple, String, Int32) pour voir si cela faisait une différence, mais ce n'était pas le cas.
Mark Watts
49
En outre, pour obtenir la valeur de la const à partir de FieldInfo, utilisez GetRawConstantValue ().
Sam Sippe
@MarkWatts a raison. Peut-être que le comportement a changé depuis sa publication. Dans tous les cas, la documentation de IsLiteraldit if its value is written at compile timeet cela n'est vrai que pour les constantes, c'est ainsi qu'il se comporte maintenant (testé à partir de .NET 4.5.2)
nawfal
52

Si vous souhaitez obtenir les valeurs de toutes les constantes d'un type spécifique, à partir du type cible, voici une méthode d'extension (étendant certaines des réponses de cette page):

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

Alors pour une classe comme celle-ci

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

Vous pouvez obtenir les stringvaleurs constantes comme ceci:

List<string> result = typeof(MyFruitKeys).GetAllPublicConstantValues<string>();
//result[0] == "apple"
//result[1] == "plum"
//result[2] == "peach"
BCA
la source
Pourquoi pas ceci .Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();:?
T-moty
17

En tant qu'extensions de type:

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
la source
1
Évidemment, c'est si vos constantes sur un type sont toutes des chaînes ;-)
bytedev
Pourquoi ne pas (a) rendre les méthodes génériques, (b) rendre les méthodes retournées IEnumerable<T>au lieu d'un IList?
Wai Ha Lee du
@WaiHaLee - Terminé :-). Bien qu'évidemment, cela suppose toujours que tous les types de consts sur la classe en question sont de type T.
bytedev
2

Utilisez property.GetConstantValue()pour obtenir de la valeur.

Reza Bayat
la source
1
Cela peut bien être le cas lorsque vous possédez la propriété - mais comment obtenir la propriété en premier lieu?
Wai Ha Lee
4
Dans .Net 4.5 c'est:GetRawConstantValue()
Chris