Como posso obter todas as constantes de um tipo por reflexão?

Respostas:

264

Embora seja um código antigo:

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

Você pode facilmente convertê-lo em código mais limpo usando genéricos 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();
}

Ou com uma linha:

type.GetFields(BindingFlags.Public | BindingFlags.Static |
               BindingFlags.FlattenHierarchy)
    .Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
gdoron está apoiando Monica
fonte
13
Meu +1 foi antes mesmo de eu passar da 2ª linha ... notei que você está passando por todas as etapas com o seu ... objetivo por design ...! isso é tão importante quando é preciso aprender com isso. Eu gostaria que todos com sua experiência fizessem o que você fez aqui.
LoneXcoder
4
Não tenho certeza sobre as afirmações com relação a IsLiteral e IsInitOnly. Nos testes, parece que, para as propriedades estáticas de somente leitura, o IsLiteral é sempre falso - portanto, o IsLiteral é o único sinalizador que você precisa verificar para encontrar constantes e pode ignorar o IsInitOnly. Eu tentei com diferentes tipos de campos (por exemplo, String, Int32) para ver se isso fazia alguma diferença, mas não.
Mark Watts
49
Além disso, para obter o valor da const do FieldInfo, use GetRawConstantValue ().
Sam Sippe
@MarkWatts está certo. Pode ser que o comportamento tenha sido alterado desde que este foi publicado. Em qualquer documentação do caso de IsLiteraldiz if its value is written at compile timee que é verdade apenas para constantes, que é como ele está se comportando agora (testado a partir de .NET 4.5.2)
Nawfal
52

Se você deseja obter os valores de todas as constantes de um tipo específico, a partir do tipo de destino, aqui está um método de extensão (estendendo algumas das respostas nesta 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();
    }
}

Então, para uma aula 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;
}

Você pode obter os stringvalores constantes assim:

List<string> result = typeof(MyFruitKeys).GetAllPublicConstantValues<string>();
//result[0] == "apple"
//result[1] == "plum"
//result[2] == "peach"
BCA
fonte
Por que não isso .Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();:?
T-moty
17

Como extensões 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
fonte
1
Obviamente, isso é se suas constantes em um tipo são todas as cordas ;-)
bytedev
Por que não (a) tornar os métodos genéricos, (b) tornar os métodos retornados em IEnumerable<T>vez de um IList?
Wai Ha Lee
@WaiHaLee - Feito :-). Embora, obviamente, ele ainda assume todos os tipos de consts na classe em questão são do tipo T.
bytedev
2

Use property.GetConstantValue()para obter valor.

Reza Bayat
fonte
1
Pode ser esse o caso quando você possui a propriedade - mas como você a obtém primeiro?
Wai Ha Lee
4
Em .Net 4.5 é:GetRawConstantValue()
Chris