¿Cómo puedo obtener todas las constantes de un tipo por reflexión?

Respuestas:

264

Aunque es un código antiguo:

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

Fuente

Puede convertirlo fácilmente a un código más limpio usando genéricos y 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 línea:

type.GetFields(BindingFlags.Public | BindingFlags.Static |
               BindingFlags.FlattenHierarchy)
    .Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
gdoron está apoyando a Monica
fuente
13
¡Mi +1 fue incluso antes de pasar la segunda línea ... noté que estás pasando por cada paso con su ... propósito intencionado por diseño ...! Esto es TAN importante cuando uno necesita aprender de él. Desearía que todos con tu experiencia hicieran lo que hiciste aquí.
LoneXcoder
44
No estoy seguro de las afirmaciones con respecto a IsLiteral e IsInitOnly. En las pruebas, parece que para las propiedades de solo lectura estática, IsLiteral siempre es falso, por lo que IsLiteral es el único indicador que debe verificar para encontrar constantes y puede ignorar IsInitOnly. Intenté con diferentes tipos de campo (por ejemplo, String, Int32) para ver si esto hacía alguna diferencia, pero no fue así.
Mark Watts,
49
Además, para obtener el valor de la constante desde FieldInfo, use GetRawConstantValue ().
Sam Sippe
@ MarkWatts tiene razón. Puede que el comportamiento haya cambiado desde que esto se publicó. En cualquier caso, la documentación de IsLiteraldice if its value is written at compile timey eso es cierto solo para constantes, que es cómo se comporta ahora (probado a partir de .NET 4.5.2)
nawfal
52

Si desea obtener los valores de todas las constantes de un tipo específico, del tipo de destino, aquí hay un método de extensión (que extiende algunas de las respuestas en esta página):

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

Entonces para una clase como esta

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

Puede obtener los stringvalores constantes de esta manera:

List<string> result = typeof(MyFruitKeys).GetAllPublicConstantValues<string>();
//result[0] == "apple"
//result[1] == "plum"
//result[2] == "peach"
BCA
fuente
¿Por qué no esto .Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();?
T-moty
17

Como extensiones de 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);
    }
}
bytedev
fuente
1
Obviamente, esto es si sus constantes en un tipo son todas cadenas ;-)
bytedev
¿Por qué no (a) hacer que los métodos sean genéricos, (b) hacer que los métodos regresen en IEnumerable<T>lugar de an IList?
Wai Ha Lee
@WaiHaLee - Hecho :-). Aunque, obviamente, todavía asume todos los tipos de consts en la clase en cuestión son de tipo T.
bytedev
2

Use property.GetConstantValue()para obtener valor.

Reza Bayat
fuente
1
Ese puede ser el caso cuando tiene la propiedad, pero ¿cómo se obtiene la propiedad por primera vez?
Wai Ha Lee
44
En .Net 4.5 es:GetRawConstantValue()
Chris